]> git.localhorst.tv Git - blank.git/blob - src/ai/Spawner.cpp
1f9573b862fca871d0d05e53a2687a5924d339e8
[blank.git] / src / ai / Spawner.cpp
1 #include "Spawner.hpp"
2
3 #include "AIController.hpp"
4
5 #include "../model/Model.hpp"
6 #include "../model/ModelRegistry.hpp"
7 #include "../rand/GaloisLFSR.hpp"
8 #include "../world/BlockLookup.hpp"
9 #include "../world/BlockType.hpp"
10 #include "../world/ChunkIndex.hpp"
11 #include "../world/Entity.hpp"
12 #include "../world/World.hpp"
13
14 #include <iostream>
15
16 using namespace std;
17
18
19 namespace blank {
20
21 Spawner::Spawner(World &world, ModelRegistry &models)
22 : world(world)
23 , models(models)
24 , entities()
25 , timer(64)
26 , despawn_range(128 * 128)
27 , spawn_distance(16 * 16)
28 , max_entities(32)
29 , chunk_range(4)
30 , model_offset(0)
31 , model_length(models.size()) {
32         timer.Start();
33 }
34
35 Spawner::~Spawner() {
36         for (Entity *e : entities) {
37                 e->UnRef();
38         }
39 }
40
41
42 void Spawner::LimitModels(size_t begin, size_t end) {
43         if (begin >= models.size() || end > models.size() || begin >= end) {
44                 cout << "warning, models limit out of bounds or invalid range given" << endl;
45         } else {
46                 model_offset = begin;
47                 model_length = end - begin;
48         }
49 }
50
51 void Spawner::Update(int dt) {
52         CheckDespawn();
53         timer.Update(dt);
54         if (timer.Hit()) {
55                 TrySpawn();
56         }
57 }
58
59
60 void Spawner::CheckDespawn() noexcept {
61         const auto &refs = world.Players();
62         for (auto iter = entities.begin(), end = entities.end(); iter != end;) {
63                 Entity &e = (**iter);
64                 if (e.Dead()) {
65                         e.UnRef();
66                         iter = entities.erase(iter);
67                         end = entities.end();
68                         continue;
69                 }
70                 bool safe = false;
71                 for (const Player &ref : refs) {
72                         glm::vec3 diff(ref.GetEntity().AbsoluteDifference(e));
73                         if (dot(diff, diff) < despawn_range) {
74                                 safe = true;
75                                 break;
76                         }
77                 }
78                 if (!safe) {
79                         e.Kill();
80                         e.UnRef();
81                         iter = entities.erase(iter);
82                         end = entities.end();
83                 } else {
84                         ++iter;
85                 }
86         }
87 }
88
89 void Spawner::TrySpawn() {
90         if (entities.size() >= max_entities || model_length == 0) return;
91
92         // select random player to punish
93         auto &players = world.Players();
94         if (players.size() == 0) return;
95         size_t player_num = world.Random().Next<unsigned short>() % players.size();
96         auto i = players.begin(), end = players.end();
97         for (; player_num > 0 && i != end; ++i, --player_num) {
98         }
99         const Player &player = *i;
100
101         BlockLookup spawn_block(player.GetChunks().RandomBlock(world.Random()));
102
103         // distance check
104         //glm::vec3 diff(glm::vec3(chunk * Chunk::Extent() - pos) + player.entity->Position());
105         //float dist = dot(diff, diff);
106         //if (dist > despawn_range || dist < spawn_distance) {
107         //      return;
108         //}
109
110         // check if the spawn block and the one above it are loaded and inhabitable
111         if (!spawn_block || spawn_block.GetType().collide_block) {
112                 return;
113         }
114
115         BlockLookup head_block(spawn_block.Next(Block::FACE_UP));
116         if (!head_block || head_block.GetType().collide_block) {
117                 return;
118         }
119
120         Spawn(player.GetEntity(), spawn_block.GetChunk().Position(), spawn_block.GetBlockCoords());
121 }
122
123 void Spawner::Spawn(Entity &reference, const glm::ivec3 &chunk, const glm::vec3 &pos) {
124         Entity &e = world.AddEntity();
125         e.Position(chunk, pos);
126         e.Bounds({ { -0.5f, -0.5f, -0.5f }, { 0.5f, 0.5f, 0.5f } });
127         e.WorldCollidable(true);
128         RandomModel().Instantiate(e.GetModel());
129         e.SetController(new AIController(world, e));
130         e.Name("spawned");
131         e.Ref();
132         entities.emplace_back(&e);
133 }
134
135 Model &Spawner::RandomModel() noexcept {
136         std::size_t offset = (world.Random().Next<std::size_t>() % model_length) + model_offset;
137         return models[offset];
138 }
139
140 }