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