]> git.localhorst.tv Git - blobs.git/blob - src/creature/goal.cpp
avoid crowded tiles
[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()), loc + seek_radius + glm::ivec2(1)));
177
178         double rating[end.y - begin.y][end.x - begin.x] { 0.0 };
179
180         // find close and rich field
181         for (int y = begin.y; y < end.y; ++y) {
182                 for (int x = begin.x; x < end.x; ++x) {
183                         const world::TileType &type = planet.TypeAt(srf, x, y);
184                         auto yield = type.FindResource(res);
185                         if (yield != type.resources.cend()) {
186                                 // TODO: subtract minimum yield
187                                 rating[y - begin.y][x - begin.x] = yield->ubiquity;
188                                 double dist = 1.0 - 0.25 * glm::length2(planet.TileCenter(srf, x, y) - pos);
189                                 rating[y - begin.y][x - begin.x] /= dist;
190                         }
191                 }
192         }
193
194         // demote crowded tiles
195         for (auto &c : planet.Creatures()) {
196                 if (&*c == &GetCreature()) continue;
197                 if (c->GetSituation().Surface() != srf) continue;
198                 glm::ivec2 coords(c->GetSituation().SurfacePosition());
199                 if (coords.x < begin.x || coords.x >= end.x) continue;
200                 if (coords.y < begin.y || coords.y >= end.y) continue;
201                 rating[coords.y - begin.y][coords.x - begin.x] *= 0.9;
202         }
203
204         glm::ivec2 best_pos(0);
205         double best_rating = -1.0;
206
207         for (int y = begin.y; y < end.y; ++y) {
208                 for (int x = begin.x; x < end.x; ++x) {
209                         if (rating[y - begin.y][x - begin.x] > best_rating) {
210                                 best_pos = glm::ivec2(x, y);
211                                 best_rating = rating[y - begin.y][x - begin.x];
212                         }
213                 }
214         }
215
216         if (best_rating) {
217                 found = true;
218                 searching = false;
219                 target_pos = planet.TileCenter(srf, best_pos.x, best_pos.y);
220                 target_srf = srf;
221                 target_tile = best_pos;
222                 GetSteering().GoTo(target_pos);
223         } else if (!searching) {
224                 found = false;
225                 searching = true;
226                 target_pos = GetSituation().Position();
227                 target_pos[(srf + 0) % 3] += Assets().random.SNorm();
228                 target_pos[(srf + 1) % 3] += Assets().random.SNorm();
229                 // bias towards current direction
230                 target_pos += glm::normalize(GetSituation().Velocity()) * 0.5;
231                 GetSteering().GoTo(target_pos);
232         }
233 }
234
235 bool LocateResourceGoal::OnTargetTile() const noexcept {
236         const Situation &s = GetSituation();
237         return s.OnSurface()
238                 && s.Surface() == target_srf
239                 && s.OnTile()
240                 && s.SurfacePosition() == target_tile;
241 }
242
243 }
244 }