示例#1
0
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;
}
示例#2
0
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;
}
示例#3
0
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);
	}
}
示例#4
0
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;
}
示例#5
0
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;
}
示例#6
0
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;
}
示例#7
0
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;
}
示例#8
0
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;
}
示例#9
0
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();
}
示例#10
0
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;
}