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