]> git.localhorst.tv Git - blank.git/blob - src/ai/Spawner.cpp
fix loop removing players from world
[blank.git] / src / ai / Spawner.cpp
1 #include "Spawner.hpp"
2
3 #include "Chaser.hpp"
4 #include "RandomWalk.hpp"
5 #include "../model/CompositeModel.hpp"
6 #include "../model/Skeletons.hpp"
7 #include "../world/BlockLookup.hpp"
8 #include "../world/BlockType.hpp"
9 #include "../world/ChunkIndex.hpp"
10 #include "../world/Entity.hpp"
11 #include "../world/World.hpp"
12
13
14 namespace blank {
15
16 Spawner::Spawner(World &world, Skeletons &skeletons, std::uint64_t seed)
17 : world(world)
18 , skeletons(skeletons)
19 , controllers()
20 , random(seed)
21 , timer(64)
22 , despawn_range(128 * 128)
23 , spawn_distance(16 * 16)
24 , max_entities(16)
25 , chunk_range(4) {
26         timer.Start();
27 }
28
29 Spawner::~Spawner() {
30         for (auto &ctrl : controllers) {
31                 delete ctrl;
32         }
33 }
34
35
36 void Spawner::Update(int dt) {
37         CheckDespawn();
38         timer.Update(dt);
39         if (timer.Hit()) {
40                 TrySpawn();
41         }
42         for (auto &ctrl : controllers) {
43                 ctrl->Update(dt);
44         }
45 }
46
47
48 void Spawner::CheckDespawn() noexcept {
49         const auto &refs = world.Players();
50         for (auto iter = controllers.begin(), end = controllers.end(); iter != end;) {
51                 Entity &e = (*iter)->Controlled();
52                 if (e.Dead()) {
53                         delete *iter;
54                         iter = controllers.erase(iter);
55                         end = controllers.end();
56                         continue;
57                 }
58                 bool safe = false;
59                 for (const Player &ref : refs) {
60                         glm::vec3 diff(ref.entity->AbsoluteDifference(e));
61                         if (dot(diff, diff) < despawn_range) {
62                                 safe = true;
63                                 break;
64                         }
65                 }
66                 if (!safe) {
67                         e.Kill();
68                         delete *iter;
69                         iter = controllers.erase(iter);
70                         end = controllers.end();
71                 } else {
72                         ++iter;
73                 }
74         }
75 }
76
77 void Spawner::TrySpawn() {
78         if (controllers.size() >= max_entities) return;
79
80         // select random player to punish
81         auto &players = world.Players();
82         if (players.size() == 0) return;
83         const Player &player = players[random.Next<unsigned short>() % players.size()];
84
85         int index = random.Next<unsigned int>() % player.chunks->TotalChunks();
86
87         glm::ivec3 chunk(player.chunks->PositionOf(index));
88
89         glm::ivec3 pos(
90                 random.Next<unsigned char>() % Chunk::width,
91                 random.Next<unsigned char>() % Chunk::height,
92                 random.Next<unsigned char>() % Chunk::depth
93         );
94
95         // distance check
96         //glm::vec3 diff(glm::vec3(chunk * Chunk::Extent() - pos) + player.entity->Position());
97         //float dist = dot(diff, diff);
98         //if (dist > despawn_range || dist < spawn_distance) {
99         //      return;
100         //}
101
102         // check if the spawn block and the one above it are loaded and inhabitable
103         BlockLookup spawn_block((*player.chunks)[index], pos);
104         if (!spawn_block || spawn_block.GetType().collide_block) {
105                 return;
106         }
107
108         BlockLookup head_block(spawn_block.Next(Block::FACE_UP));
109         if (!head_block || head_block.GetType().collide_block) {
110                 return;
111         }
112
113         Spawn(*player.entity, chunk, glm::vec3(pos) + glm::vec3(0.5f));
114 }
115
116 void Spawner::Spawn(Entity &reference, const glm::ivec3 &chunk, const glm::vec3 &pos) {
117         glm::vec3 rot(0.000001f);
118         rot.x *= (random.Next<unsigned short>() % 1024);
119         rot.y *= (random.Next<unsigned short>() % 1024);
120         rot.z *= (random.Next<unsigned short>() % 1024);
121
122         Entity &e = world.AddEntity();
123         e.Name("spawned");
124         e.Position(chunk, pos);
125         e.Bounds({ { -0.5f, -0.5f, -0.5f }, { 0.5f, 0.5f, 0.5f } });
126         e.WorldCollidable(true);
127         skeletons[random.Next<unsigned char>() % skeletons.Size()].Instantiate(e.GetModel());
128         e.AngularVelocity(rot);
129         Controller *ctrl;
130         if (random()) {
131                 ctrl = new RandomWalk(e, random.Next<std::uint64_t>());
132         } else {
133                 ctrl = new Chaser(world, e, reference);
134         }
135         controllers.emplace_back(ctrl);
136 }
137
138 }