]> git.localhorst.tv Git - blobs.git/blob - src/creature/goal.cpp
some tweaks
[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         // check if eligible to split
92         if (GetCreature().Mass() > GetCreature().GetProperties().Birth().mass * 1.8) {
93                 double fert = GetCreature().Fertility();
94                 double rand = Assets().random.UNorm();
95                 if (fert > rand) {
96                         std::cout << "[" << int(GetCreature().GetSimulation().Time())
97                                 << "s] " << GetCreature().Name() << " split" << std::endl;
98                         Split(GetCreature());
99                 }
100         }
101 }
102
103
104 LocateResourceGoal::LocateResourceGoal(Creature &c, int res)
105 : Goal(c)
106 , res(res)
107 , found(false)
108 , target_pos(0.0)
109 , target_srf(0)
110 , target_tile(0)
111 , searching(false)
112 , reevaluate(0.0) {
113 }
114
115 LocateResourceGoal::~LocateResourceGoal() noexcept {
116 }
117
118 std::string LocateResourceGoal::Describe() const {
119         return "locate " + GetCreature().GetSimulation().Resources()[res].name;
120 }
121
122 void LocateResourceGoal::Enable() {
123
124 }
125
126 void LocateResourceGoal::Tick(double dt) {
127         reevaluate -= dt;
128 }
129
130 void LocateResourceGoal::Action() {
131         if (reevaluate < 0.0) {
132                 LocateResource();
133                 reevaluate = 3.0;
134         } else if (!found) {
135                 if (!searching) {
136                         LocateResource();
137                 } else {
138                         double dist = glm::length2(GetSituation().Position() - target_pos);
139                         if (dist < 0.0001) {
140                                 LocateResource();
141                         } else {
142                                 GetSteering().GoTo(target_pos);
143                         }
144                 }
145         } else if (OnTargetTile()) {
146                 GetSteering().Halt();
147                 if (!GetSituation().Moving()) {
148                         SetComplete();
149                 }
150         } else {
151                 GetSteering().GoTo(target_pos);
152         }
153         GetSteering().Haste(Urgency());
154 }
155
156 void LocateResourceGoal::LocateResource() {
157         if (GetSituation().OnSurface()) {
158                 const world::TileType &t = GetSituation().GetTileType();
159                 auto yield = t.FindResource(res);
160                 if (yield != t.resources.cend()) {
161                         // hoooray
162                         GetSteering().Halt();
163                         found = true;
164                         searching = false;
165                         target_pos = GetSituation().Position();
166                         target_srf = GetSituation().Surface();
167                         target_tile = GetSituation().GetPlanet().SurfacePosition(target_srf, target_pos);
168                 } else {
169                         // go find somewhere else
170                         SearchVicinity();
171                 }
172         } else {
173                 // well, what now?
174         }
175 }
176
177 void LocateResourceGoal::SearchVicinity() {
178         const world::Planet &planet = GetSituation().GetPlanet();
179         int srf = GetSituation().Surface();
180         const glm::dvec3 &pos = GetSituation().Position();
181
182         glm::ivec2 loc = planet.SurfacePosition(srf, pos);
183         glm::ivec2 seek_radius(2);
184         glm::ivec2 begin(glm::max(glm::ivec2(0), loc - seek_radius));
185         glm::ivec2 end(glm::min(glm::ivec2(planet.SideLength()), loc + seek_radius + glm::ivec2(1)));
186
187         double rating[end.y - begin.y][end.x - begin.x] { 0.0 };
188
189         // find close and rich field
190         for (int y = begin.y; y < end.y; ++y) {
191                 for (int x = begin.x; x < end.x; ++x) {
192                         const world::TileType &type = planet.TypeAt(srf, x, y);
193                         auto yield = type.FindResource(res);
194                         if (yield != type.resources.cend()) {
195                                 // TODO: subtract minimum yield
196                                 rating[y - begin.y][x - begin.x] = yield->ubiquity;
197                                 double dist = std::max(0.125, 0.25 * glm::length(planet.TileCenter(srf, x, y) - pos));
198                                 rating[y - begin.y][x - begin.x] /= dist;
199                         }
200                 }
201         }
202
203         // demote crowded tiles
204         for (auto &c : planet.Creatures()) {
205                 if (&*c == &GetCreature()) continue;
206                 if (c->GetSituation().Surface() != srf) continue;
207                 glm::ivec2 coords(c->GetSituation().SurfacePosition());
208                 if (coords.x < begin.x || coords.x >= end.x) continue;
209                 if (coords.y < begin.y || coords.y >= end.y) continue;
210                 rating[coords.y - begin.y][coords.x - begin.x] *= 0.9;
211         }
212
213         glm::ivec2 best_pos(0);
214         double best_rating = -1.0;
215
216         for (int y = begin.y; y < end.y; ++y) {
217                 for (int x = begin.x; x < end.x; ++x) {
218                         if (rating[y - begin.y][x - begin.x] > best_rating) {
219                                 best_pos = glm::ivec2(x, y);
220                                 best_rating = rating[y - begin.y][x - begin.x];
221                         }
222                 }
223         }
224
225         if (best_rating) {
226                 found = true;
227                 searching = false;
228                 target_pos = planet.TileCenter(srf, best_pos.x, best_pos.y);
229                 target_srf = srf;
230                 target_tile = best_pos;
231                 GetSteering().GoTo(target_pos);
232         } else if (!searching) {
233                 found = false;
234                 searching = true;
235                 target_pos = GetSituation().Position();
236                 target_pos[(srf + 0) % 3] += Assets().random.SNorm();
237                 target_pos[(srf + 1) % 3] += Assets().random.SNorm();
238                 // bias towards current direction
239                 target_pos += glm::normalize(GetSituation().Velocity()) * 0.5;
240                 target_pos = clamp(target_pos, -planet.Radius(), planet.Radius());
241                 GetSteering().GoTo(target_pos);
242         }
243 }
244
245 bool LocateResourceGoal::OnTargetTile() const noexcept {
246         const Situation &s = GetSituation();
247         return s.OnSurface()
248                 && s.Surface() == target_srf
249                 && s.OnTile()
250                 && s.SurfacePosition() == target_tile;
251 }
252
253 }
254 }