]> git.localhorst.tv Git - blobs.git/blob - src/creature/goal.cpp
avoid others when steering
[blobs.git] / src / creature / goal.cpp
1 #include "Goal.hpp"
2 #include "IdleGoal.hpp"
3 #include "LocateResourceGoal.hpp"
4
5 #include "Creature.hpp"
6 #include "../app/Assets.hpp"
7 #include "../world/Planet.hpp"
8 #include "../world/Resource.hpp"
9 #include "../world/Simulation.hpp"
10 #include "../world/TileType.hpp"
11
12 #include <iostream>
13 #include <glm/gtx/io.hpp>
14
15
16 namespace blobs {
17 namespace creature {
18
19 Goal::Goal(Creature &c)
20 : c(c)
21 , on_complete()
22 , urgency(0.0)
23 , interruptible(true)
24 , complete(false) {
25 }
26
27 Goal::~Goal() noexcept {
28 }
29
30 Situation &Goal::GetSituation() noexcept {
31         return c.GetSituation();
32 }
33
34 const Situation &Goal::GetSituation() const noexcept {
35         return c.GetSituation();
36 }
37
38 Steering &Goal::GetSteering() noexcept {
39         return c.GetSteering();
40 }
41
42 const Steering &Goal::GetSteering() const noexcept {
43         return c.GetSteering();
44 }
45
46 app::Assets &Goal::Assets() noexcept {
47         return c.GetSimulation().Assets();
48 }
49
50 const app::Assets &Goal::Assets() const noexcept {
51         return c.GetSimulation().Assets();
52 }
53
54 void Goal::SetComplete() noexcept {
55         if (!complete) {
56                 complete = true;
57                 if (on_complete) {
58                         on_complete(*this);
59                 }
60         }
61 }
62
63 void Goal::OnComplete(std::function<void(Goal &)> cb) noexcept {
64         on_complete = cb;
65         if (complete) {
66                 on_complete(*this);
67         }
68 }
69
70
71 IdleGoal::IdleGoal(Creature &c)
72 : Goal(c) {
73         Urgency(-1.0);
74         Interruptible(true);
75 }
76
77 IdleGoal::~IdleGoal() {
78 }
79
80 std::string IdleGoal::Describe() const {
81         return "idle";
82 }
83
84 void IdleGoal::Enable() {
85 }
86
87 void IdleGoal::Tick(double dt) {
88 }
89
90 void IdleGoal::Action() {
91         double fert = GetCreature().Fertility();
92         double rand = Assets().random.UNorm();
93         if (fert > rand) {
94                 std::cout << "[" << int(GetCreature().GetSimulation().Time())
95                         << "s] " << GetCreature().Name() << " split" << std::endl;
96                 Split(GetCreature());
97         }
98 }
99
100
101 LocateResourceGoal::LocateResourceGoal(Creature &c, int res)
102 : Goal(c)
103 , res(res)
104 , found(false)
105 , target_pos(0.0)
106 , target_srf(0)
107 , target_tile(0)
108 , searching(false) {
109 }
110
111 LocateResourceGoal::~LocateResourceGoal() noexcept {
112 }
113
114 std::string LocateResourceGoal::Describe() const {
115         return "locate " + GetCreature().GetSimulation().Resources()[res].name;
116 }
117
118 void LocateResourceGoal::Enable() {
119         LocateResource();
120 }
121
122 void LocateResourceGoal::Tick(double dt) {
123 }
124
125 void LocateResourceGoal::Action() {
126         if (!found) {
127                 if (!searching) {
128                         LocateResource();
129                 } else {
130                         double dist = glm::length2(GetSituation().Position() - target_pos);
131                         if (dist < 0.0001) {
132                                 LocateResource();
133                         } else {
134                                 GetSteering().GoTo(target_pos);
135                         }
136                 }
137         } else if (OnTargetTile()) {
138                 GetSteering().Halt();
139                 if (!GetSituation().Moving()) {
140                         SetComplete();
141                 }
142         } else {
143                 GetSteering().GoTo(target_pos);
144         }
145 }
146
147 void LocateResourceGoal::LocateResource() {
148         if (GetSituation().OnSurface()) {
149                 const world::TileType &t = GetSituation().GetTileType();
150                 auto yield = t.FindResource(res);
151                 if (yield != t.resources.cend()) {
152                         // hoooray
153                         GetSteering().Halt();
154                         found = true;
155                         searching = false;
156                         target_pos = GetSituation().Position();
157                         target_srf = GetSituation().Surface();
158                         target_tile = GetSituation().GetPlanet().SurfacePosition(target_srf, target_pos);
159                 } else {
160                         // go find somewhere else
161                         SearchVicinity();
162                 }
163         } else {
164                 // well, what now?
165         }
166 }
167
168 void LocateResourceGoal::SearchVicinity() {
169         const world::Planet &planet = GetSituation().GetPlanet();
170         int srf = GetSituation().Surface();
171         const glm::dvec3 &pos = GetSituation().Position();
172
173         glm::ivec2 loc = planet.SurfacePosition(srf, pos);
174         glm::ivec2 seek_radius(2);
175         glm::ivec2 begin(glm::max(glm::ivec2(0), loc - seek_radius));
176         glm::ivec2 end(glm::min(glm::ivec2(planet.SideLength() - 1), loc + seek_radius));
177
178         const world::TileType::Yield *best = nullptr;
179         glm::ivec2 best_pos;
180         double best_distance;
181
182         for (int y = begin.y; y <= end.y; ++y) {
183                 for (int x = begin.x; x <= end.x; ++x) {
184                         const world::TileType &type = planet.TypeAt(srf, x, y);
185                         auto yield = type.FindResource(res);
186                         if (yield != type.resources.cend()) {
187                                 double dist = glm::length2(planet.TileCenter(srf, x, y) - pos);
188                                 if (!best) {
189                                         best = &*yield;
190                                         best_pos = glm::ivec2(x, y);
191                                         best_distance = dist;
192                                 } else if (yield->ubiquity - (dist * 0.125) > best->ubiquity - (best_distance * 0.125)) {
193                                         best = &*yield;
194                                         best_pos = glm::ivec2(x, y);
195                                         best_distance = dist;
196                                 }
197                         }
198                 }
199         }
200         if (best) {
201                 found = true;
202                 searching = false;
203                 target_pos = planet.TileCenter(srf, best_pos.x, best_pos.y);
204                 target_srf = srf;
205                 target_tile = best_pos;
206                 GetSteering().GoTo(target_pos);
207         } else if (!searching) {
208                 found = false;
209                 searching = true;
210                 target_pos = GetSituation().Position();
211                 target_pos[(srf + 0) % 3] += Assets().random.SNorm();
212                 target_pos[(srf + 1) % 3] += Assets().random.SNorm();
213                 // bias towards current direction
214                 target_pos += glm::normalize(GetSituation().Velocity()) * 0.5;
215                 GetSteering().GoTo(target_pos);
216         }
217 }
218
219 bool LocateResourceGoal::OnTargetTile() const noexcept {
220         const Situation &s = GetSituation();
221         return s.OnSurface()
222                 && s.Surface() == target_srf
223                 && s.OnTile()
224                 && s.SurfacePosition() == target_tile;
225 }
226
227 }
228 }