]> git.localhorst.tv Git - blobs.git/blob - src/creature/goal.cpp
9d3e1254c870670e15db2f38eb28d08358af57e6
[blobs.git] / src / creature / goal.cpp
1 #include "BlobBackgroundTask.hpp"
2 #include "Goal.hpp"
3 #include "IdleGoal.hpp"
4 #include "LocateResourceGoal.hpp"
5
6 #include "Creature.hpp"
7 #include "../app/Assets.hpp"
8 #include "../world/Planet.hpp"
9 #include "../world/Resource.hpp"
10 #include "../world/Simulation.hpp"
11 #include "../world/TileType.hpp"
12
13 #include <iostream>
14 #include <glm/gtx/io.hpp>
15
16
17 namespace blobs {
18 namespace creature {
19
20 BlobBackgroundTask::BlobBackgroundTask(Creature &c)
21 : Goal(c) {
22 }
23
24 BlobBackgroundTask::~BlobBackgroundTask() {
25 }
26
27 std::string BlobBackgroundTask::Describe() const {
28         return "being a blob";
29 }
30
31 void BlobBackgroundTask::Tick(double dt) {
32 }
33
34 void BlobBackgroundTask::Action() {
35         // check if eligible to split
36         if (GetCreature().Mass() > GetCreature().GetProperties().Birth().mass * 1.8) {
37                 double fert = GetCreature().Fertility();
38                 double rand = Assets().random.UNorm();
39                 if (fert > rand) {
40                         std::cout << "[" << int(GetCreature().GetSimulation().Time())
41                                 << "s] " << GetCreature().Name() << " split" << std::endl;
42                         Split(GetCreature());
43                         return;
44                 }
45         }
46         // check for random property mutation
47         if (GetCreature().Mutability() > Assets().random.UNorm()) {
48                 double amount = 1.0 + (Assets().random.SNorm() * 0.05);
49                 auto &props = GetCreature().GetGenome().properties;
50                 double r = Assets().random.UNorm();
51                 math::Distribution *d = nullptr;
52                 if (Assets().random.UNorm() < 0.5) {
53                         auto &set = props.props[(int(Assets().random.UNorm() * 4.0) % 4) + 1];
54                         if (r < 0.25) {
55                                 d = &set.age;
56                         } else if (r < 0.5) {
57                                 d = &set.mass;
58                         } else if (r < 0.75) {
59                                 d = &set.fertility;
60                         } else {
61                                 d = &set.highlight;
62                         }
63                 } else {
64                         if (r < 0.2) {
65                                 d = &props.strength;
66                         } else if (r < 0.4) {
67                                 d = &props.stamina;
68                         } else if (r < 0.6) {
69                                 d = &props.dexerty;
70                         } else if (r < 0.8) {
71                                 d = &props.intelligence;
72                         } else {
73                                 d = &props.mutability;
74                         }
75                 }
76                 if (Assets().random.UNorm() < 0.5) {
77                         d->Mean(d->Mean() * amount);
78                 } else {
79                         d->StandardDeviation(d->StandardDeviation() * amount);
80                 }
81         }
82 }
83
84
85 Goal::Goal(Creature &c)
86 : c(c)
87 , on_complete()
88 , urgency(0.0)
89 , interruptible(true)
90 , complete(false) {
91 }
92
93 Goal::~Goal() noexcept {
94 }
95
96 Situation &Goal::GetSituation() noexcept {
97         return c.GetSituation();
98 }
99
100 const Situation &Goal::GetSituation() const noexcept {
101         return c.GetSituation();
102 }
103
104 Steering &Goal::GetSteering() noexcept {
105         return c.GetSteering();
106 }
107
108 const Steering &Goal::GetSteering() const noexcept {
109         return c.GetSteering();
110 }
111
112 app::Assets &Goal::Assets() noexcept {
113         return c.GetSimulation().Assets();
114 }
115
116 const app::Assets &Goal::Assets() const noexcept {
117         return c.GetSimulation().Assets();
118 }
119
120 void Goal::SetComplete() noexcept {
121         if (!complete) {
122                 complete = true;
123                 if (on_complete) {
124                         on_complete(*this);
125                 }
126         }
127 }
128
129 void Goal::OnComplete(std::function<void(Goal &)> cb) noexcept {
130         on_complete = cb;
131         if (complete) {
132                 on_complete(*this);
133         }
134 }
135
136
137 IdleGoal::IdleGoal(Creature &c)
138 : Goal(c) {
139         Urgency(-1.0);
140         Interruptible(true);
141 }
142
143 IdleGoal::~IdleGoal() {
144 }
145
146 std::string IdleGoal::Describe() const {
147         return "idle";
148 }
149
150 void IdleGoal::Enable() {
151 }
152
153 void IdleGoal::Tick(double dt) {
154 }
155
156 void IdleGoal::Action() {
157 }
158
159
160 LocateResourceGoal::LocateResourceGoal(Creature &c, int res)
161 : Goal(c)
162 , res(res)
163 , found(false)
164 , target_pos(0.0)
165 , target_srf(0)
166 , target_tile(0)
167 , searching(false)
168 , reevaluate(0.0) {
169 }
170
171 LocateResourceGoal::~LocateResourceGoal() noexcept {
172 }
173
174 std::string LocateResourceGoal::Describe() const {
175         return "locate " + GetCreature().GetSimulation().Resources()[res].name;
176 }
177
178 void LocateResourceGoal::Enable() {
179
180 }
181
182 void LocateResourceGoal::Tick(double dt) {
183         reevaluate -= dt;
184 }
185
186 void LocateResourceGoal::Action() {
187         if (reevaluate < 0.0) {
188                 LocateResource();
189                 reevaluate = 3.0;
190         } else if (!found) {
191                 if (!searching) {
192                         LocateResource();
193                 } else {
194                         double dist = glm::length2(GetSituation().Position() - target_pos);
195                         if (dist < 0.0001) {
196                                 LocateResource();
197                         } else {
198                                 GetSteering().GoTo(target_pos);
199                         }
200                 }
201         } else if (OnTargetTile()) {
202                 GetSteering().Halt();
203                 if (!GetSituation().Moving()) {
204                         SetComplete();
205                 }
206         } else {
207                 GetSteering().GoTo(target_pos);
208         }
209         GetSteering().Haste(Urgency());
210 }
211
212 void LocateResourceGoal::LocateResource() {
213         if (GetSituation().OnSurface()) {
214                 const world::TileType &t = GetSituation().GetTileType();
215                 auto yield = t.FindResource(res);
216                 if (yield != t.resources.cend()) {
217                         // hoooray
218                         GetSteering().Halt();
219                         found = true;
220                         searching = false;
221                         target_pos = GetSituation().Position();
222                         target_srf = GetSituation().Surface();
223                         target_tile = GetSituation().GetPlanet().SurfacePosition(target_srf, target_pos);
224                 } else {
225                         // go find somewhere else
226                         SearchVicinity();
227                 }
228         } else {
229                 // well, what now?
230         }
231 }
232
233 void LocateResourceGoal::SearchVicinity() {
234         const world::Planet &planet = GetSituation().GetPlanet();
235         int srf = GetSituation().Surface();
236         const glm::dvec3 &pos = GetSituation().Position();
237
238         glm::ivec2 loc = planet.SurfacePosition(srf, pos);
239         glm::ivec2 seek_radius(2);
240         glm::ivec2 begin(glm::max(glm::ivec2(0), loc - seek_radius));
241         glm::ivec2 end(glm::min(glm::ivec2(planet.SideLength()), loc + seek_radius + glm::ivec2(1)));
242
243         double rating[end.y - begin.y][end.x - begin.x] { 0.0 };
244
245         // find close and rich field
246         for (int y = begin.y; y < end.y; ++y) {
247                 for (int x = begin.x; x < end.x; ++x) {
248                         const world::TileType &type = planet.TypeAt(srf, x, y);
249                         auto yield = type.FindResource(res);
250                         if (yield != type.resources.cend()) {
251                                 // TODO: subtract minimum yield
252                                 rating[y - begin.y][x - begin.x] = yield->ubiquity;
253                                 double dist = std::max(0.125, 0.25 * glm::length(planet.TileCenter(srf, x, y) - pos));
254                                 rating[y - begin.y][x - begin.x] /= dist;
255                         }
256                 }
257         }
258
259         // demote crowded tiles
260         for (auto &c : planet.Creatures()) {
261                 if (&*c == &GetCreature()) continue;
262                 if (c->GetSituation().Surface() != srf) continue;
263                 glm::ivec2 coords(c->GetSituation().SurfacePosition());
264                 if (coords.x < begin.x || coords.x >= end.x) continue;
265                 if (coords.y < begin.y || coords.y >= end.y) continue;
266                 rating[coords.y - begin.y][coords.x - begin.x] *= 0.9;
267         }
268
269         glm::ivec2 best_pos(0);
270         double best_rating = -1.0;
271
272         for (int y = begin.y; y < end.y; ++y) {
273                 for (int x = begin.x; x < end.x; ++x) {
274                         if (rating[y - begin.y][x - begin.x] > best_rating) {
275                                 best_pos = glm::ivec2(x, y);
276                                 best_rating = rating[y - begin.y][x - begin.x];
277                         }
278                 }
279         }
280
281         if (best_rating) {
282                 found = true;
283                 searching = false;
284                 target_pos = planet.TileCenter(srf, best_pos.x, best_pos.y);
285                 target_srf = srf;
286                 target_tile = best_pos;
287                 GetSteering().GoTo(target_pos);
288         } else if (!searching) {
289                 found = false;
290                 searching = true;
291                 target_pos = GetSituation().Position();
292                 target_pos[(srf + 0) % 3] += Assets().random.SNorm();
293                 target_pos[(srf + 1) % 3] += Assets().random.SNorm();
294                 // bias towards current direction
295                 target_pos += glm::normalize(GetSituation().Velocity()) * 0.5;
296                 target_pos = clamp(target_pos, -planet.Radius(), planet.Radius());
297                 GetSteering().GoTo(target_pos);
298         }
299 }
300
301 bool LocateResourceGoal::OnTargetTile() const noexcept {
302         const Situation &s = GetSituation();
303         return s.OnSurface()
304                 && s.Surface() == target_srf
305                 && s.OnTile()
306                 && s.SurfacePosition() == target_tile;
307 }
308
309 }
310 }