bool BuldingProducer::place(Unit *unit, Terrain *terrain, coord::tile init_tile) { /* * decide what terrain is passable using this lambda * currently unit avoids water and tiles with another unit * this function should be true if pos is a valid position of the object */ auto passable = [=](const coord::phys3 &pos) -> bool { // look at all tiles in the bases range for (coord::tile check_pos : tile_list(unit->location->get_range(pos))) { TileContent *tc = terrain->get_data(check_pos); if (!tc) return false; if (!tc->obj.empty()) return false; } return true; }; // buildings have a square baase unit->location = new SquareObject(unit, passable, this->size, this->terrain_outline); // try to place the obj, it knows best whether it will fit. coord::phys3 init_pos = init_tile.to_phys2().to_phys3(); bool obj_placed = unit->location->place(terrain, init_pos); if (obj_placed) { this->on_create->play(); if (this->foundation_terrain > 0) { // TODO: use the gamedata terrain lookup! unit->location->set_ground(this->foundation_terrain, 0); } } return obj_placed; }
TerrainObject *UnitType::place_beside(Unit *u, TerrainObject const *other) const { if (!u || !other) { return nullptr; } // find the range of possible tiles tile_range outline{other->pos.start - coord::tile_delta{1, 1}, other->pos.end + coord::tile_delta{1, 1}, other->pos.draw}; // find a free position adjacent to the object auto terrain = other->get_terrain(); for (coord::tile temp_pos : tile_list(outline)) { TerrainChunk *chunk = terrain->get_chunk(temp_pos); if (chunk == nullptr) { continue; } auto placed = this->place(u, terrain, temp_pos.to_phys2().to_phys3()); if (placed) { return placed; } } return nullptr; }
void TerrainObject::place_unchecked(std::shared_ptr<Terrain> t, coord::phys3 &position) { // storing the position: this->pos = get_range(position); this->terrain = t; this->occupied_chunk_count = 0; bool chunk_known = false; // set pointers to this object on each terrain tile // where the building will stand and block the ground for (coord::tile temp_pos : tile_list(this->pos)) { TerrainChunk *chunk = this->get_terrain()->get_chunk(temp_pos); if (chunk == nullptr) { continue; } for (int c = 0; c < this->occupied_chunk_count; c++) { if (this->occupied_chunk[c] == chunk) { chunk_known = true; } } if (not chunk_known) { this->occupied_chunk[this->occupied_chunk_count] = chunk; this->occupied_chunk_count += 1; } else { chunk_known = false; } int tile_pos = chunk->tile_position_neigh(temp_pos); chunk->get_data(tile_pos)->obj.push_back(this); } }
void TerrainObject::remove() { // remove all children first for (auto &c : this->children) { c->remove(); } this->children.clear(); if (this->occupied_chunk_count == 0 || this->state == object_state::removed) { return; } for (coord::tile temp_pos : tile_list(this->pos)) { TerrainChunk *chunk = this->get_terrain()->get_chunk(temp_pos); if (chunk == nullptr) { continue; } int tile_pos = chunk->tile_position_neigh(temp_pos); auto &v = chunk->get_data(tile_pos)->obj; auto position_it = std::remove_if( std::begin(v), std::end(v), [this](TerrainObject *obj) { return this == obj; }); v.erase(position_it, std::end(v)); } this->occupied_chunk_count = 0; this->state = object_state::removed; }
TerrainObject *ProjectileProducer::place(Unit *u, std::shared_ptr<Terrain> terrain, coord::phys3 init_pos) const { /* * radial base shape without collision checking */ u->make_location<RadialObject>(this->unit_data.radius_size1, this->terrain_outline); TerrainObject *obj_ptr = u->location.get(); std::weak_ptr<Terrain> terrain_ptr = terrain; u->location->passable = [obj_ptr, u, terrain_ptr](const coord::phys3 &pos) -> bool { if (pos.up > 64000) { return true; } // avoid intersections with launcher Unit *launcher = nullptr; auto terrain = terrain_ptr.lock(); if (u->has_attribute(attr_type::projectile)) { auto &pr_attr = u->get_attribute<attr_type::projectile>(); if (pr_attr.launched && pr_attr.launcher.is_valid()) { launcher = pr_attr.launcher.get(); } else { return true; } } else { return true; } // look at all tiles in the bases range for (coord::tile check_pos : tile_list(obj_ptr->get_range(pos))) { TileContent *tc = terrain->get_data(check_pos); if (!tc) return false; // ensure no intersections with other objects for (auto obj_cmp : tc->obj) { if (obj_ptr != obj_cmp && &obj_cmp->unit != launcher && obj_cmp->check_collisions() && obj_ptr->intersects(*obj_cmp, pos)) { return false; } } } return true; }; u->location->draw = [u]() { u->draw(); }; // try to place the obj, it knows best whether it will fit. if (u->location->place(terrain, init_pos, object_state::placed_no_collision)) { return u->location.get(); } return nullptr; }
bool SquareObject::contains(const coord::phys3 &other) const { coord::tile other_tile = other.to_tile3().to_tile(); for (coord::tile check_pos : tile_list(this->pos)) { if (check_pos == other_tile) { return true; } } return false; }
bool TerrainObject::place(object_state init_state) { if (this->state == object_state::removed) { throw Error(MSG(err) << "Building cannot change state with no position"); } // remove any other floating objects // which intersect with the new placement // if non-floating objects are on the foundation // then this placement will fail for (coord::tile temp_pos : tile_list(this->pos)) { std::vector<TerrainObject *> to_remove; TerrainChunk *chunk = this->get_terrain()->get_chunk(temp_pos); if (chunk == nullptr) { continue; } for (auto obj : chunk->get_data(temp_pos)->obj) { // ignore self and annexes of self if (obj != this && obj->get_parent() != this) { if (obj->is_floating()) { // floating objects get removed to_remove.push_back(obj); } else if (obj->check_collisions()) { // solid objects obstruct placement return false; } } } // all obstructing objects get deleted for (auto remove_obj : to_remove) { remove_obj->unit.location = nullptr; } } // set new state this->state = init_state; return true; }
bool UnitTypeTest::place(Unit *unit, Terrain *terrain, coord::tile init_tile) { /* * decide what terrain is passable using this lambda * currently unit avoids water and tiles with another unit * this function should be true if pos is a valid position of the object */ auto passable = [=](const coord::phys3 &pos) -> bool { // look at all tiles in the bases range for (coord::tile check_pos : tile_list(unit->location->get_range(pos))) { TileContent *tc = terrain->get_data(check_pos); if (!tc) return false; // not allowed on water if (tc->terrain_id == 1 || tc->terrain_id == 14 || tc->terrain_id == 15) return false; // ensure no intersections with other objects for (auto obj_cmp : tc->obj) { if (unit->location != obj_cmp && unit->location->intersects(obj_cmp, pos)) { return false; } } } return true; }; /* * radial base shape */ unit->location = new RadialObject(unit, passable, this->unit_data.radius_size1, this->terrain_outline); // try to place the obj, it knows best whether it will fit. coord::phys3 init_pos = init_tile.to_phys2().to_phys3(); bool obj_placed = unit->location->place(terrain, init_pos); if (obj_placed) { this->on_create->play(); } return obj_placed; }
TerrainObject *BuildingProducer::place(Unit *u, std::shared_ptr<Terrain> terrain, coord::phys3 init_pos) const { // buildings have a square base u->make_location<SquareObject>(this->foundation_size, this->terrain_outline); /* * decide what terrain is passable using this lambda * currently unit avoids water and tiles with another unit * this function should be true if pos is a valid position of the object */ TerrainObject *obj_ptr = u->location.get(); std::weak_ptr<Terrain> terrain_ptr = terrain; u->location->passable = [obj_ptr, terrain_ptr](const coord::phys3 &pos) -> bool { auto terrain = terrain_ptr.lock(); // look at all tiles in the bases range for (coord::tile check_pos : tile_list(obj_ptr->get_range(pos))) { TileContent *tc = terrain->get_data(check_pos); if (!tc) { return false; } for (auto tobj : tc->obj) { if (tobj->check_collisions()) return false; } } return true; }; // drawing function bool draw_outline = this->enable_collisions; u->location->draw = [u, obj_ptr, draw_outline]() { if (u->selected && draw_outline) { obj_ptr->draw_outline(); } u->draw(); }; // try to place the obj, it knows best whether it will fit. auto state = object_state::floating; if (!u->location->place(terrain, init_pos, state)) { return nullptr; } // annex objects for (unsigned i = 0; i < 4; ++i) { const gamedata::building_annex &annex = this->unit_data.building_annex.data[i]; if (annex.unit_id > 0) { // make objects for annex coord::phys3 a_pos = u->location->pos.draw; a_pos.ne += annex.misplaced0 * coord::settings::phys_per_tile; a_pos.se += annex.misplaced1 * coord::settings::phys_per_tile; this->make_annex(*u, terrain, annex.unit_id, a_pos, i == 0); } } // TODO: play sound once built if (this->on_create) { this->on_create->play(); } return u->location.get(); }
TerrainObject *ObjectProducer::place(Unit *u, std::shared_ptr<Terrain> terrain, coord::phys3 init_pos) const { // create new object with correct base shape if (this->unit_data.selection_shape > 1) { u->make_location<RadialObject>(this->unit_data.radius_size0, this->terrain_outline); } else { u->make_location<SquareObject>(this->foundation_size, this->terrain_outline); } // find set of allowed terrains std::unordered_set<terrain_t> terrains = allowed_terrains(this->unit_data.terrain_restriction); /* * decide what terrain is passable using this lambda * currently unit avoids water and tiles with another unit * this function should be true if pos is a valid position of the object */ TerrainObject *obj_ptr = u->location.get(); std::weak_ptr<Terrain> terrain_ptr = terrain; u->location->passable = [obj_ptr, terrain_ptr, terrains](const coord::phys3 &pos) -> bool { // if location is deleted, then so is this lambda (deleting terrain implies location is deleted) // so locking objects here will not return null auto terrain = terrain_ptr.lock(); // look at all tiles in the bases range for (coord::tile check_pos : tile_list(obj_ptr->get_range(pos))) { TileContent *tc = terrain->get_data(check_pos); // invalid tile types if (!tc || terrains.count(tc->terrain_id) == 0) { return false; } // compare with objects intersecting the units tile // ensure no intersections with other objects for (auto obj_cmp : tc->obj) { if (obj_ptr != obj_cmp && obj_cmp->check_collisions() && obj_ptr->intersects(*obj_cmp, pos)) { return false; } } } return true; }; u->location->draw = [u, obj_ptr]() { if (u->selected) { obj_ptr->draw_outline(); } u->draw(); }; // try to place the obj, it knows best whether it will fit. auto state = this->decay? object_state::placed_no_collision : object_state::placed; if (u->location->place(terrain, init_pos, state)) { if (this->on_create) { this->on_create->play(); } return u->location.get(); } // placing at the given position failed u->log(MSG(dbg) << "failed to place object"); return nullptr; }