#include "Chaser.hpp"
#include "RandomWalk.hpp"
+#include "../world/BlockLookup.hpp"
#include "../world/BlockType.hpp"
#include "../world/BlockTypeRegistry.hpp"
#include "../world/Entity.hpp"
Spawner::Spawner(World &world)
: world(world)
, controllers()
-, timer(8096)
+, timer(64)
, despawn_range(128 * 128)
, spawn_distance(16 * 16)
, max_entities(16)
return;
}
- // block check
- // TODO: avoid force load, abort spawn if chunk unavailble
- Chunk &tgt_chunk = world.Next(world.PlayerChunk(), chunk);
- // TODO: don't use visibility for spawn check
- // also, check for more than one block space
- if (tgt_chunk.Type(tgt_chunk.BlockAt(pos)).visible) {
+ // check if the spawn block and the one above it are loaded and inhabitable
+ BlockLookup spawn_block(&world.PlayerChunk(), chunk * Chunk::Extent() + pos);
+ if (!spawn_block || spawn_block.GetType().collide_block) {
+ return;
+ }
+
+ BlockLookup head_block(spawn_block.Next(Block::FACE_UP));
+ if (!head_block || head_block.GetType().collide_block) {
return;
}
void WorldSave::Read(World::Config &conf) const {
- cout << "reading world save" << endl;
-
ifstream in(conf_path);
if (!in) {
throw runtime_error("failed to open world config");
}
void WorldSave::Write(const World::Config &conf) const {
- cout << "writing world save" << endl;
-
if (!make_dirs(root_path)) {
throw runtime_error("failed to create world save directory");
}
istream::sentry s(in);
if (!s) {
- // TODO: error?
+ throw runtime_error("read past the end of stream");
return;
}
case ',': case '=':
current.type = Token::Type(c);
break;
- case '+': case '-':
+ case '+': case '-': case '.':
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
in.putback(c);
namespace {
bool is_num_char(istream::char_type c) {
- return isdigit(c)
+ return isxdigit(c)
|| c == '.'
|| c == '-'
|| c == '+'
}
+template<class T>
+T manhattan_distance(const glm::tvec3<T> &a, const glm::tvec3<T> &b) {
+ glm::tvec3<T> diff(abs(a - b));
+ return diff.x + diff.y + diff.z;
+}
+
+template<class T>
+T manhattan_radius(const glm::tvec3<T> &v) {
+ glm::tvec3<T> a(abs(v));
+ return std::max(a.x, std::max(a.y, a.z));
+}
+
+
struct AABB {
glm::vec3 min;
glm::vec3 max;
8, 9, 9, 11, 11, 10, 10 , 8, // top
0, 8, 4, 10, 2, 6, // verticals, btf
1, 9, 5, 11, 3, 7,
- // 5, 8, 7, 10,
- // 1, 9, 3, 11,
});
}
const glm::mat4 &M,
const AABB &box,
const glm::mat4 &box_M,
- float &depth,
+ float &dist,
glm::vec3 &normal
) const noexcept {
- // TODO: this is wrong, but simple. so for now will have to do
- return Intersection(bot, M, box, box_M, depth, normal) || Intersection(top, M, box, box_M, depth, normal);
+ bool top_hit, bot_hit;
+ float top_dist, bot_dist;
+ glm::vec3 top_normal, bot_normal;
+
+ top_hit = Intersection(bot, M, box, box_M, top_dist, top_normal);
+ bot_hit = Intersection(top, M, box, box_M, bot_dist, bot_normal);
+
+ if (top_hit) {
+ if (bot_hit && bot_dist < top_dist) {
+ dist = bot_dist;
+ normal = bot_normal;
+ return true;
+ } else {
+ dist = top_dist;
+ normal = top_normal;
+ return true;
+ }
+ return true;
+ } else if (bot_hit) {
+ dist = bot_dist;
+ normal = bot_normal;
+ return true;
+ } else {
+ return false;
+ }
}
}
AABB box = e.Bounds();
glm::mat4 M = e.Transform(player->ChunkCoords());
bool any = false;
- // TODO: this only needs to check the chunks surrounding the entity's chunk position
- // need find out if that is quicker than the rough chunk bounds test
for (Chunk &cur_chunk : chunks.Loaded()) {
+ if (manhattan_radius(cur_chunk.Position() - e.ChunkCoords()) > 1) {
+ // chunk is not one of the 3x3x3 surrounding the entity
+ // since there's no entity which can extent over 16 blocks, they can be skipped
+ continue;
+ }
if (cur_chunk.Intersection(box, M, cur_chunk.Transform(player->ChunkCoords()), col)) {
any = true;
}