]> git.localhorst.tv Git - blobs.git/blob - src/world/sim.cpp
load universe from file
[blobs.git] / src / world / sim.cpp
1 #include "Record.hpp"
2 #include "Simulation.hpp"
3
4 #include "Body.hpp"
5 #include "Planet.hpp"
6 #include "Sun.hpp"
7 #include "../creature/Creature.hpp"
8 #include "../ui/string.hpp"
9
10 #include <algorithm>
11 #include <iostream>
12
13
14 namespace blobs {
15 namespace world {
16
17 int Record::Update(creature::Creature &c, double value, double time) noexcept {
18         int found = -1;
19         for (int i = 0; i < MAX; ++i) {
20                 if (value > rank[i].value) {
21                         found = i;
22                         break;
23                 }
24         }
25         if (found < 0) {
26                 return -1;
27         }
28         int previous = -1;
29         for (int i = 0; i < MAX; ++i) {
30                 if (rank[i].holder == &c) {
31                         previous = i;
32                         break;
33                 }
34         }
35         if (previous < 0) {
36                 // move all below down by one
37                 std::copy_backward(rank + found, rank + MAX - 1, rank + MAX);
38         } else if (found > previous) {
39                 // better than last, but not an improvement
40                 // this ensures only one slot occupied per creature
41                 return found;
42         } else if (found < previous) {
43                 // move all in between down by one
44                 std::copy_backward(rank + found, rank + previous, rank + previous + 1);
45         }
46         // insert new
47         rank[found].holder = &c;
48         rank[found].value = value;
49         rank[found].time = time;
50         return found;
51 }
52
53 std::string Record::ValueString(int i) const {
54         if (i < 0 || i >= MAX || !rank[i].holder) {
55                 return "—";
56         }
57         switch (type) {
58                 default:
59                 case VALUE:
60                         return ui::DecimalString(rank[i].value, 2);
61                 case LENGTH:
62                         return ui::LengthString(rank[i].value);
63                 case MASS:
64                         return ui::MassString(rank[i].value);
65                 case PERCENTAGE:
66                         return ui::PercentageString(rank[i].value);
67                 case TIME:
68                         return ui::TimeString(rank[i].value);
69         }
70 }
71
72 Simulation::Simulation(app::Assets &assets)
73 : assets(assets)
74 , bodies()
75 , planets()
76 , suns()
77 , alive()
78 , dead()
79 , time(0.0)
80 , records(7) {
81         records[0].name = "Age";
82         records[0].type = Record::TIME;
83         records[1].name = "Mass";
84         records[1].type = Record::MASS;
85         records[2].name = "Size";
86         records[2].type = Record::LENGTH;
87         records[3].name = "Strength";
88         records[4].name = "Stamina";
89         records[5].name = "Dexerty";
90         records[6].name = "Intelligence";
91 }
92
93 Simulation::~Simulation() {
94         for (auto c : alive) {
95                 delete c;
96         }
97         for (auto c : dead) {
98                 delete c;
99         }
100 }
101
102 void Simulation::Tick(double dt) {
103         time += dt;
104         for (auto body : bodies) {
105                 body->Tick(dt);
106         }
107         for (auto c : alive) {
108                 CheckRecords(*c);
109         }
110 }
111
112 void Simulation::AddBody(Body &b) {
113         b.SetSimulation(*this);
114         bodies.insert(&b);
115 }
116
117 void Simulation::AddPlanet(Planet &p) {
118         AddBody(p);
119         planets.insert(&p);
120 }
121
122 void Simulation::AddSun(Sun &s) {
123         AddBody(s);
124         suns.insert(&s);
125 }
126
127 Planet &Simulation::PlanetByName(const std::string &name) {
128         for (auto &p : planets) {
129                 if (p->Name() == name) {
130                         return *p;
131                 }
132         }
133         throw std::runtime_error("planet named \"" + name + "\" not found");
134 }
135
136 void Simulation::SetAlive(creature::Creature *c) {
137         alive.push_back(c);
138 }
139
140 void Simulation::SetDead(creature::Creature *c) {
141         auto entry = std::find(alive.begin(), alive.end(), c);
142         if (entry != alive.end()) {
143                 alive.erase(entry);
144         }
145         dead.push_back(c);
146         CheckRecords(*c);
147 }
148
149 void Simulation::CheckRecords(creature::Creature &c) noexcept {
150         { // age
151                 creature::Creature *prev = records[0].rank[0].holder;
152                 int rank = records[0].Update(c, c.Age(), time);
153                 if (rank == 0 && prev && prev != &c) {
154                         LogRecord(records[0]);
155                 }
156         }
157         { // mass
158                 creature::Creature *prev = records[1].rank[0].holder;
159                 int rank = records[1].Update(c, c.Mass(), time);
160                 if (rank == 0 && prev && prev != &c) {
161                         LogRecord(records[1]);
162                 }
163         }
164         { // size
165                 creature::Creature *prev = records[2].rank[0].holder;
166                 int rank = records[2].Update(c, c.Size(), time);
167                 if (rank == 0 && prev && prev != &c) {
168                         LogRecord(records[2]);
169                 }
170         }
171         { // strength
172                 creature::Creature *prev = records[3].rank[0].holder;
173                 int rank = records[3].Update(c, c.Strength(), time);
174                 if (rank == 0 && prev && prev != &c) {
175                         LogRecord(records[3]);
176                 }
177         }
178         { // stamina
179                 creature::Creature *prev = records[4].rank[0].holder;
180                 int rank = records[4].Update(c, c.Stamina(), time);
181                 if (rank == 0 && prev && prev != &c) {
182                         LogRecord(records[4]);
183                 }
184         }
185         { // dexerty
186                 creature::Creature *prev = records[5].rank[0].holder;
187                 int rank = records[5].Update(c, c.Dexerty(), time);
188                 if (rank == 0 && prev && prev != &c) {
189                         LogRecord(records[5]);
190                 }
191         }
192         { // intelligence
193                 creature::Creature *prev = records[6].rank[0].holder;
194                 int rank = records[6].Update(c, c.Intelligence(), time);
195                 if (rank == 0 && prev && prev != &c) {
196                         LogRecord(records[6]);
197                 }
198         }
199 }
200
201 void Simulation::LogRecord(const Record &r) {
202         Log() << "at age " << ui::TimeString(r.rank[0].holder->Age()) << " "
203                 << r.rank[0].holder->Name() << " broke the " << r.name << " record of "
204                 << r.ValueString(1) << " by " << r.rank[1].holder->Name()
205                 << " (established " << ui::TimeString(r.rank[1].time) << ")" << std::endl;
206 }
207
208 std::ostream &Simulation::Log() {
209         return std::cout << '[' << ui::TimeString(Time()) << "] ";
210 }
211
212 }
213 }