#include "common.hpp" #include namespace world { sector_index_t sector_index_at(v2f_t x) { return sector_index_t((x / SECTOR_SIZE).floor()); } tile_index_t tile_index_at(v2f_t x) { return sector_index_t(x.floor()); } world_t::world_t(void) { prng.seed(125); perlin.generate(&prng, 32); } void world_t::generate_tile(tile_t *tile, tile_index_t x) { float waterlevel, height; waterlevel = perlin.get(x, 1000.0f) * 0.3f + perlin.get(x, 500.0f) * 0.1f; height = perlin.get(x, 40.0f) * 0.6f + perlin.get(x, 20.0f) * 0.25f + perlin.get(x, 10.0f) * 0.2f + perlin.get(x, 4.0f) * 0.1f + perlin.get(x, 1.0f) * 0.05f; if (height < waterlevel) tile->type = TILE_NONE; else if (height < waterlevel + 0.1) tile->type = TILE_DIRT; else if (perlin.get(x, 3.0f) > 0.0f) tile->type = TILE_WALL; else tile->type = TILE_DIRT; } void world_t::generate(sector_t *sector, sector_index_t index, bool partial) { sector->index = index; sector->bounds.v[0] = (v2f_t)index * SECTOR_SIZE; sector->bounds.v[1] = sector->bounds.v[0] + v2f_t(SECTOR_SIZE, SECTOR_SIZE); for (coord_t ly = 0; ly < SECTOR_SIZE; ly++) for (coord_t lx = 0; lx < SECTOR_SIZE; lx++) generate_tile(sector->tiles + ly * SECTOR_SIZE + lx, tile_index_t(index[0] * SECTOR_SIZE + lx, index[1] * SECTOR_SIZE + ly)); sector->empty = false; if (partial) return; for (coord_t ly = 0; ly < SECTOR_SIZE; ly++) for (coord_t lx = 0; lx < SECTOR_SIZE; lx++) { tile_t *tile; tile = sector->tiles + ly * SECTOR_SIZE + lx; tile->neighbors = 0; for (size_t i = 0; i < 8; i++) { tile_index_t neighbor_index; tile_t *neighbor; neighbor_index = index * SECTOR_SIZE + tile_index_t(lx, ly) + neighbor_offsets[i]; neighbor = get_tile(neighbor_index, true); if (neighbor->type == tile->type) tile->neighbors |= (1 << i); } } } bool world_t::find_path(v2f_t src, v2f_t dst, cmodel_t *cmodel, entity_t *ignore, std::list *path) { path_finder_t finder; rectf_t bounds; v2f_t cmodel_dims; finder.setup_nodes(src, dst, cmodel->cflags); cmodel_dims = cmodel->bounds.dims(); for (size_t y = 0; y < finder.height; y++) for (size_t x = 0; x < finder.width; x++) { tile_index_t index; rectf_t combined; index = finder.base + tile_index_t(x, y); if (get_tile(index)->type == TILE_DIRT) continue; combined[0] = v2f_t(index) - cmodel_dims / 2; combined[1] = v2f_t(index) + v2f_t(1.0f, 1.0f) + cmodel_dims / 2; finder.eliminate_nodes(combined); } bounds = rectf_t(src, dst).norm(); for (entity_t *ent : get_entities(bounds, cmodel->cflags)) if (ent != ignore) finder.eliminate_nodes(ent->cmodel.bounds); if (!finder.find()) return false; finder.export_path(path); return true; } sector_t *world_t::get_sector(sector_index_t index, bool partial) { sector_t *sector; sector = §ors[index]; if (sector->empty) generate(sector, index, partial); return sector; } tile_t *world_t::get_tile(tile_index_t index, bool partial) { sector_index_t sector_index; sector_t *sector; int64_t tx, ty; sector_index[0] = divide_rmi(index[0], (int64_t)SECTOR_SIZE, &tx); sector_index[1] = divide_rmi(index[1], (int64_t)SECTOR_SIZE, &ty); sector = get_sector(sector_index, partial); return sector->tiles + ty * SECTOR_SIZE + tx; } std::list world_t::get_sectors(rectf_t rect) { sector_index_t base, upper; std::list list; base = sector_index_at(rect.v[0]); upper = sector_index_at(rect.v[1]); for (int64_t y = base[1]; y <= upper[1]; y++) for (int64_t x = base[0]; x <= upper[0]; x++) { sector_index_t index(x, y); list.push_back(get_sector(index)); } return list; } std::list world_t::get_entities(rectf_t rect, cflags_t cflags) { static size_t cookie = 0; std::list list; cookie++; for (sector_t *sector : get_sectors(rect)) for (entity_t *ent : sector->ents) { if (ent->cookie == cookie) continue; ent->cookie = cookie; if (!(ent->cmodel.cflags & cflags)) continue; if (!(rect && ent->cmodel.bounds)) continue; list.push_back(ent); } return list; } std::list world_t::get_render_entities(rectf_t rect) { static size_t cookie = 0; std::list list; cookie++; for (sector_t *sector : get_sectors(rect)) for (entity_t *ent : sector->ents) { if (ent->cookie == cookie) continue; if (!(rect && ent->render_bounds)) continue; ent->cookie = cookie; list.push_back(ent); } return list; } bool world_t::test_rect(const cmodel_t *cmodel, const entity_t *ignore) { static size_t cookie = 0; cookie++; for (sector_t *sector : get_sectors(cmodel->bounds)) { rect_t bounds; tile_index_t index; bounds[0] = (cmodel->bounds[0] - sector->bounds[0]).floor(); bounds[1] = (cmodel->bounds[1] - sector->bounds[0]).floor(); if (bounds[0][0] < 0) bounds[0][0] = 0; if (bounds[0][1] < 0) bounds[0][1] = 0; if (bounds[1][0] >= (coord_t)SECTOR_SIZE) bounds[1][0] = SECTOR_SIZE - 1; if (bounds[1][1] >= (coord_t)SECTOR_SIZE) bounds[1][1] = SECTOR_SIZE - 1; for (index[1] = bounds[0][1]; index[1] <= bounds[1][1]; index[1]++) for (index[0] = bounds[0][0]; index[0] <= bounds[1][0]; index[0]++) { tile_t *tile; tile = sector->tiles + index[1] * SECTOR_SIZE + index[0]; if (tile->type != TILE_DIRT) return true; } for (entity_t *ent : sector->ents) { if (ent == ignore) continue; if (ent->cookie == cookie) continue; ent->cookie = cookie; if (!(ent->cmodel.cflags & cmodel->cflags)) continue; if (!(cmodel->bounds && ent->cmodel.bounds)) continue; return true; } } return false; } void world_t::debug_point(sf::Vector2f point) { sector_index_t index = sector_index_at(point); printf("sector (%zd, %zd)\n", index[0], index[1]); } entity_t::entity_t(int type_) { type = type_; } void entity_t::link_to_sector(sector_t *sector) { parents.push_back(sector); sector->ents.insert(this); } void entity_t::link(world_t *world) { rectf_t total_bounds; float fx, fy; sector_index_t base; float xlip, ylip; size_t xsecs, ysecs; total_bounds = cmodel.bounds | render_bounds; fx = floor(total_bounds[0][0]); fy = floor(total_bounds[0][1]); base = sector_index_at(v2f_t(fx, fy)); xlip = total_bounds[1][0] - (base[0] + 1) * SECTOR_SIZE; ylip = total_bounds[1][1] - (base[1] + 1) * SECTOR_SIZE; if (xlip > 0.0f) xsecs = ceil(xlip / SECTOR_SIZE) + 1; else xsecs = 1; if (ylip > 0.0f) ysecs = ceil(ylip / SECTOR_SIZE) + 1; else ysecs = 1; for (int64_t y = 0; y < (int64_t)ysecs; y++) for (int64_t x = 0; x < (int64_t)xsecs; x++) { sector_index_t index = base + sector_index_t(x, y); sector_t *sector; sector = world->get_sector(index); link_to_sector(sector); } } void entity_t::unlink(void) { for (sector_t *sector : parents) { if (sector->ents.find(this) == sector->ents.end()) { printf("entity_t::unlink: %p should belong to %p (%" PRIi64", %" PRIi64") but it doesn't\n", this, sector, sector->index[0], sector->index[1]); continue; } sector->ents.erase(sector->ents.find(this)); } parents.clear(); parent_world = nullptr; } } // namespace world