Objects can now be placed under tiles that have non-block collision
This commit is contained in:
parent
edbee201ee
commit
a7ae034278
@ -32,9 +32,9 @@ bool ObjectOrientation::placementValid(World const* world, Vec2I const& position
|
||||
if (!world)
|
||||
return false;
|
||||
|
||||
for (auto space : spaces) {
|
||||
for (Vec2I space : spaces) {
|
||||
space += position;
|
||||
if (world->tileIsOccupied(space, TileLayer::Foreground) || world->isTileProtected(space))
|
||||
if (world->tileIsOccupied(space, TileLayer::Foreground, false, true) || world->isTileProtected(space))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
@ -238,10 +238,16 @@ EntityPtr WorldClient::findEntityAtTile(Vec2I const& pos, EntityFilterOf<TileEnt
|
||||
return m_entityMap->findEntityAtTile(pos, entityFilter);
|
||||
}
|
||||
|
||||
bool WorldClient::tileIsOccupied(Vec2I const& pos, TileLayer layer, bool includeEphemeral) const {
|
||||
bool WorldClient::tileIsOccupied(Vec2I const& pos, TileLayer layer, bool includeEphemeral, bool checkCollision) const {
|
||||
if (!inWorld())
|
||||
return false;
|
||||
return WorldImpl::tileIsOccupied(m_tileArray, m_entityMap, pos, layer, includeEphemeral);
|
||||
return WorldImpl::tileIsOccupied(m_tileArray, m_entityMap, pos, layer, includeEphemeral, checkCollision);
|
||||
}
|
||||
|
||||
CollisionKind WorldClient::tileCollisionKind(Vec2I const& pos) const {
|
||||
if (!inWorld())
|
||||
return CollisionKind::Null;
|
||||
return WorldImpl::tileCollisionKind(m_tileArray, m_entityMap, pos);
|
||||
}
|
||||
|
||||
void WorldClient::forEachCollisionBlock(RectI const& region, function<void(CollisionBlock const&)> const& iterator) const {
|
||||
|
@ -63,7 +63,8 @@ public:
|
||||
EntityPtr findEntity(RectF const& boundBox, EntityFilter entityFilter) const override;
|
||||
EntityPtr findEntityLine(Vec2F const& begin, Vec2F const& end, EntityFilter entityFilter) const override;
|
||||
EntityPtr findEntityAtTile(Vec2I const& pos, EntityFilterOf<TileEntity> entityFilter) const override;
|
||||
bool tileIsOccupied(Vec2I const& pos, TileLayer layer, bool includeEphemeral = false) const override;
|
||||
bool tileIsOccupied(Vec2I const& pos, TileLayer layer, bool includeEphemeral = false, bool checkCollision = false) const override;
|
||||
CollisionKind tileCollisionKind(Vec2I const& pos) const override;
|
||||
void forEachCollisionBlock(RectI const& region, function<void(CollisionBlock const&)> const& iterator) const override;
|
||||
bool isTileConnectable(Vec2I const& pos, TileLayer layer, bool tilesOnly = false) const override;
|
||||
bool pointTileCollision(Vec2F const& point, CollisionSet const& collisionSet = DefaultCollisionSet) const override;
|
||||
|
@ -21,7 +21,11 @@ namespace Star {
|
||||
namespace WorldImpl {
|
||||
template <typename TileSectorArray>
|
||||
bool tileIsOccupied(shared_ptr<TileSectorArray> const& tileSectorArray, EntityMapPtr const& entityMap,
|
||||
Vec2I const& pos, TileLayer layer, bool includeEphemeral);
|
||||
Vec2I const& pos, TileLayer layer, bool includeEphemeral, bool checkCollision = false);
|
||||
|
||||
template <typename TileSectorArray>
|
||||
CollisionKind tileCollisionKind(shared_ptr<TileSectorArray> const& tileSectorArray, EntityMapPtr const& entityMap,
|
||||
Vec2I const& pos);
|
||||
|
||||
template <typename TileSectorArray>
|
||||
bool rectTileCollision(shared_ptr<TileSectorArray> const& tileSectorArray, RectI const& region, bool solidCollision);
|
||||
@ -38,13 +42,13 @@ namespace WorldImpl {
|
||||
|
||||
template <typename GetTileFunction>
|
||||
bool canPlaceMaterial(EntityMapPtr const& entityMap,
|
||||
Vec2I const& pos, TileLayer layer, MaterialId material, bool allowEntityOverlap, GetTileFunction& getTile);
|
||||
Vec2I const& pos, TileLayer layer, MaterialId material, bool allowEntityOverlap, bool allowTileOverlap, GetTileFunction& getTile);
|
||||
// returns true if this material could be placed if in the same batch other
|
||||
// tiles can be placed
|
||||
// that connect to it
|
||||
template <typename GetTileFunction>
|
||||
bool perhapsCanPlaceMaterial(EntityMapPtr const& entityMap,
|
||||
Vec2I const& pos, TileLayer layer, MaterialId material, bool allowEntityOverlap, GetTileFunction& getTile);
|
||||
Vec2I const& pos, TileLayer layer, MaterialId material, bool allowEntityOverlap, bool allowTileOverlap, GetTileFunction& getTile);
|
||||
template <typename GetTileFunction>
|
||||
bool canPlaceMaterialColorVariant(Vec2I const& pos, TileLayer layer, MaterialColorVariant color, GetTileFunction& getTile);
|
||||
template <typename GetTileFunction>
|
||||
@ -80,11 +84,18 @@ namespace WorldImpl {
|
||||
|
||||
template <typename TileSectorArray>
|
||||
bool tileIsOccupied(shared_ptr<TileSectorArray> const& tileSectorArray, EntityMapPtr const& entityMap,
|
||||
Vec2I const& pos, TileLayer layer, bool includeEphemeral) {
|
||||
Vec2I const& pos, TileLayer layer, bool includeEphemeral, bool checkCollision) {
|
||||
auto& tile = tileSectorArray->tile(pos);
|
||||
if (layer == TileLayer::Foreground)
|
||||
return tileSectorArray->tile(pos).foreground != EmptyMaterialId || entityMap->tileIsOccupied(pos, includeEphemeral);
|
||||
return (checkCollision ? tile.collision >= CollisionKind::Dynamic : tile.foreground != EmptyMaterialId) || entityMap->tileIsOccupied(pos, includeEphemeral);
|
||||
else
|
||||
return tileSectorArray->tile(pos).background != EmptyMaterialId;
|
||||
return tile.background != EmptyMaterialId;
|
||||
}
|
||||
|
||||
template <typename TileSectorArray>
|
||||
CollisionKind tileCollisionKind(shared_ptr<TileSectorArray> const& tileSectorArray, EntityMapPtr const& entityMap,
|
||||
Vec2I const& pos) {
|
||||
return tileSectorArray->tile(pos).collision;
|
||||
}
|
||||
|
||||
template <typename TileSectorArray>
|
||||
@ -210,7 +221,7 @@ namespace WorldImpl {
|
||||
|
||||
template <typename GetTileFunction>
|
||||
bool canPlaceMaterial(EntityMapPtr const& entityMap,
|
||||
Vec2I const& pos, TileLayer layer, MaterialId material, bool allowEntityOverlap, GetTileFunction& getTile) {
|
||||
Vec2I const& pos, TileLayer layer, MaterialId material, bool allowEntityOverlap, bool allowTileOverlap, GetTileFunction& getTile) {
|
||||
auto materialDatabase = Root::singleton().materialDatabase();
|
||||
|
||||
if (!isRealMaterial(material))
|
||||
@ -239,26 +250,26 @@ namespace WorldImpl {
|
||||
if (!materialDatabase->canPlaceInLayer(material, layer))
|
||||
return false;
|
||||
|
||||
auto& tile = getTile(pos);
|
||||
if (layer == TileLayer::Background) {
|
||||
if (getTile(pos).background != EmptyMaterialId)
|
||||
if (tile.background != EmptyMaterialId && tile.background != ObjectPlatformMaterialId)
|
||||
return false;
|
||||
|
||||
// Can attach background blocks to other background blocks, *or* the
|
||||
// foreground block in front of it.
|
||||
if (!isAdjacentToConnectable(pos, 1, false)
|
||||
&& !isConnectableMaterial(getTile({pos[0], pos[1]}).foreground))
|
||||
if (!isAdjacentToConnectable(pos, 1, false) && !isConnectableMaterial(tile.foreground))
|
||||
return false;
|
||||
} else {
|
||||
if (getTile(pos).foreground != EmptyMaterialId)
|
||||
if (tile.foreground != EmptyMaterialId && tile.foreground != ObjectPlatformMaterialId)
|
||||
return false;
|
||||
|
||||
if (entityMap->tileIsOccupied(pos))
|
||||
if (!allowTileOverlap && entityMap->tileIsOccupied(pos))
|
||||
return false;
|
||||
|
||||
if (!allowEntityOverlap && entityMap->spaceIsOccupied(RectF::withSize(Vec2F(pos), Vec2F(1, 1))))
|
||||
if (!allowEntityOverlap && entityMap->spaceIsOccupied(RectF::withSize(Vec2F(pos), Vec2F(0.999f, 0.999f))))
|
||||
return false;
|
||||
|
||||
if (!isAdjacentToConnectable(pos, 1, true) && !isConnectableMaterial(getTile({pos[0], pos[1]}).background))
|
||||
if (!isAdjacentToConnectable(pos, 1, true) && !isConnectableMaterial(tile.background))
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -267,7 +278,7 @@ namespace WorldImpl {
|
||||
|
||||
template <typename GetTileFunction>
|
||||
bool perhapsCanPlaceMaterial(EntityMapPtr const& entityMap,
|
||||
Vec2I const& pos, TileLayer layer, MaterialId material, bool allowEntityOverlap, GetTileFunction& getTile) {
|
||||
Vec2I const& pos, TileLayer layer, MaterialId material, bool allowEntityOverlap, bool allowTileOverlap, GetTileFunction& getTile) {
|
||||
auto materialDatabase = Root::singleton().materialDatabase();
|
||||
|
||||
if (!isRealMaterial(material))
|
||||
@ -276,17 +287,18 @@ namespace WorldImpl {
|
||||
if (!materialDatabase->canPlaceInLayer(material, layer))
|
||||
return false;
|
||||
|
||||
auto& tile = getTile(pos);
|
||||
if (layer == TileLayer::Background) {
|
||||
if (getTile(pos).background != EmptyMaterialId)
|
||||
if (tile.background != EmptyMaterialId && tile.background != ObjectPlatformMaterialId)
|
||||
return false;
|
||||
} else {
|
||||
if (getTile(pos).foreground != EmptyMaterialId)
|
||||
if (tile.foreground != EmptyMaterialId && tile.foreground != ObjectPlatformMaterialId)
|
||||
return false;
|
||||
|
||||
if (entityMap->tileIsOccupied(pos))
|
||||
if (!allowTileOverlap && entityMap->tileIsOccupied(pos))
|
||||
return false;
|
||||
|
||||
if (!allowEntityOverlap && entityMap->spaceIsOccupied(RectF::withSize(Vec2F(pos), Vec2F(1, 1))))
|
||||
if (!allowEntityOverlap && entityMap->spaceIsOccupied(RectF::withSize(Vec2F(pos), Vec2F(0.999f, 0.999f))))
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -321,9 +333,10 @@ namespace WorldImpl {
|
||||
bool perhaps = false;
|
||||
|
||||
if (auto placeMaterial = modification.ptr<PlaceMaterial>()) {
|
||||
perhaps = WorldImpl::perhapsCanPlaceMaterial(entityMap, pos, placeMaterial->layer, placeMaterial->material, allowEntityOverlap, getTile);
|
||||
bool allowTileOverlap = placeMaterial->collisionOverride != TileCollisionOverride::None && collisionKindFromOverride(placeMaterial->collisionOverride) < CollisionKind::Dynamic;
|
||||
perhaps = WorldImpl::perhapsCanPlaceMaterial(entityMap, pos, placeMaterial->layer, placeMaterial->material, allowEntityOverlap, allowTileOverlap, getTile);
|
||||
if (perhaps)
|
||||
good = WorldImpl::canPlaceMaterial(entityMap, pos, placeMaterial->layer, placeMaterial->material, allowEntityOverlap, getTile);
|
||||
good = WorldImpl::canPlaceMaterial(entityMap, pos, placeMaterial->layer, placeMaterial->material, allowEntityOverlap, allowTileOverlap, getTile);
|
||||
} else if (auto placeMod = modification.ptr<PlaceMod>()) {
|
||||
good = WorldImpl::canPlaceMod(pos, placeMod->layer, placeMod->mod, getTile);
|
||||
} else if (auto placeMaterialColor = modification.ptr<PlaceMaterialColor>()) {
|
||||
|
@ -756,10 +756,15 @@ EntityPtr WorldServer::findEntityAtTile(Vec2I const& pos, EntityFilterOf<TileEnt
|
||||
return m_entityMap->findEntityAtTile(pos, entityFilter);
|
||||
}
|
||||
|
||||
bool WorldServer::tileIsOccupied(Vec2I const& pos, TileLayer layer, bool includeEphemeral) const {
|
||||
return WorldImpl::tileIsOccupied(m_tileArray, m_entityMap, pos, layer, includeEphemeral);
|
||||
bool WorldServer::tileIsOccupied(Vec2I const& pos, TileLayer layer, bool includeEphemeral, bool checkCollision) const {
|
||||
return WorldImpl::tileIsOccupied(m_tileArray, m_entityMap, pos, layer, includeEphemeral, checkCollision);
|
||||
}
|
||||
|
||||
CollisionKind WorldServer::tileCollisionKind(Vec2I const& pos) const {
|
||||
return WorldImpl::tileCollisionKind(m_tileArray, m_entityMap, pos);
|
||||
}
|
||||
|
||||
|
||||
void WorldServer::forEachCollisionBlock(RectI const& region, function<void(CollisionBlock const&)> const& iterator) const {
|
||||
const_cast<WorldServer*>(this)->freshenCollision(region);
|
||||
m_tileArray->tileEach(region, [iterator](Vec2I const& pos, ServerTile const& tile) {
|
||||
@ -1348,7 +1353,8 @@ TileModificationList WorldServer::doApplyTileModifications(TileModificationList
|
||||
continue;
|
||||
|
||||
if (auto placeMaterial = modification.ptr<PlaceMaterial>()) {
|
||||
if (!WorldImpl::canPlaceMaterial(m_entityMap, pos, placeMaterial->layer, placeMaterial->material, allowEntityOverlap, m_tileGetterFunction))
|
||||
bool allowTileOverlap = placeMaterial->collisionOverride != TileCollisionOverride::None && collisionKindFromOverride(placeMaterial->collisionOverride) < CollisionKind::Dynamic;
|
||||
if (!WorldImpl::canPlaceMaterial(m_entityMap, pos, placeMaterial->layer, placeMaterial->material, allowEntityOverlap, allowTileOverlap, m_tileGetterFunction))
|
||||
continue;
|
||||
|
||||
ServerTile* tile = m_tileArray->modifyTile(pos);
|
||||
@ -1498,14 +1504,21 @@ void WorldServer::updateTileEntityTiles(TileEntityPtr const& entity, bool removi
|
||||
|
||||
ServerTile* tile = m_tileArray->modifyTile(pos);
|
||||
if (tile) {
|
||||
bool updated = false;
|
||||
if (tile->foreground == materialSpace.material) {
|
||||
tile->foreground = EmptyMaterialId;
|
||||
tile->foregroundMod = NoModId;
|
||||
tile->rootSource = {};
|
||||
if (tile->updateCollision(materialDatabase->materialCollisionKind(tile->foreground))) {
|
||||
updated = true;
|
||||
}
|
||||
if (tile->collision == materialDatabase->materialCollisionKind(materialSpace.material)
|
||||
&& tile->updateCollision(materialSpace.prevCollision.value(CollisionKind::None))) {
|
||||
m_liquidEngine->visitLocation(pos);
|
||||
m_fallingBlocksAgent->visitLocation(pos);
|
||||
dirtyCollision(RectI::withSize(pos, { 1, 1 }));
|
||||
updated = true;
|
||||
}
|
||||
if (updated)
|
||||
queueTileUpdates(pos);
|
||||
}
|
||||
}
|
||||
@ -1515,24 +1528,37 @@ void WorldServer::updateTileEntityTiles(TileEntityPtr const& entity, bool removi
|
||||
|
||||
} else {
|
||||
// add new material spaces and update the known material spaces entry
|
||||
List<MaterialSpace> passedSpaces;
|
||||
for (auto const& materialSpace : newMaterialSpaces) {
|
||||
Vec2I pos = materialSpace.space + entity->tilePosition();
|
||||
|
||||
bool updated = false;
|
||||
bool updatedCollision = false;
|
||||
ServerTile* tile = m_tileArray->modifyTile(pos);
|
||||
if (tile) {
|
||||
if (tile && (tile->foreground == EmptyMaterialId || tile->foreground == materialSpace.material)) {
|
||||
tile->foreground = materialSpace.material;
|
||||
tile->foregroundMod = NoModId;
|
||||
if (isRealMaterial(materialSpace.material))
|
||||
tile->rootSource = entity->tilePosition();
|
||||
if (tile->updateCollision(materialDatabase->materialCollisionKind(tile->foreground))) {
|
||||
passedSpaces.emplaceAppend(materialSpace).prevCollision.emplace(tile->collision);
|
||||
updatedCollision = tile->updateCollision(materialDatabase->materialCollisionKind(tile->foreground));
|
||||
updated = true;
|
||||
passedSpaces.emplaceAppend(materialSpace);
|
||||
}
|
||||
else if (tile && tile->collision < CollisionKind::Dynamic) {
|
||||
passedSpaces.emplaceAppend(materialSpace).prevCollision.emplace(tile->collision);
|
||||
updatedCollision = tile->updateCollision(materialDatabase->materialCollisionKind(materialSpace.material));
|
||||
updated = true;
|
||||
}
|
||||
if (updatedCollision) {
|
||||
m_liquidEngine->visitLocation(pos);
|
||||
m_fallingBlocksAgent->visitLocation(pos);
|
||||
dirtyCollision(RectI::withSize(pos, { 1, 1 }));
|
||||
}
|
||||
if (updated)
|
||||
queueTileUpdates(pos);
|
||||
}
|
||||
}
|
||||
spaces.materials = move(newMaterialSpaces);
|
||||
spaces.materials = move(passedSpaces);
|
||||
|
||||
// add new roots and update known roots entry
|
||||
for (auto const& rootPos : newRoots) {
|
||||
|
@ -144,7 +144,8 @@ public:
|
||||
EntityPtr findEntity(RectF const& boundBox, EntityFilter entityFilter) const override;
|
||||
EntityPtr findEntityLine(Vec2F const& begin, Vec2F const& end, EntityFilter entityFilter) const override;
|
||||
EntityPtr findEntityAtTile(Vec2I const& pos, EntityFilterOf<TileEntity> entityFilter) const override;
|
||||
bool tileIsOccupied(Vec2I const& pos, TileLayer layer, bool includeEphemeral = false) const override;
|
||||
bool tileIsOccupied(Vec2I const& pos, TileLayer layer, bool includeEphemeral = false, bool checkCollision = false) const override;
|
||||
CollisionKind tileCollisionKind(Vec2I const& pos) const override;
|
||||
void forEachCollisionBlock(RectI const& region, function<void(CollisionBlock const&)> const& iterator) const override;
|
||||
bool isTileConnectable(Vec2I const& pos, TileLayer layer, bool tilesOnly = false) const override;
|
||||
bool pointTileCollision(Vec2F const& point, CollisionSet const& collisionSet = DefaultCollisionSet) const override;
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include "StarEntity.hpp"
|
||||
#include "StarTileDamage.hpp"
|
||||
#include "StarInteractiveEntity.hpp"
|
||||
#include "StarCollisionBlock.hpp"
|
||||
|
||||
namespace Star {
|
||||
|
||||
@ -17,6 +18,7 @@ struct MaterialSpace {
|
||||
|
||||
Vec2I space;
|
||||
MaterialId material;
|
||||
Maybe<CollisionKind> prevCollision;
|
||||
};
|
||||
|
||||
DataStream& operator<<(DataStream& ds, MaterialSpace const& materialSpace);
|
||||
@ -89,7 +91,9 @@ inline MaterialSpace::MaterialSpace(Vec2I space, MaterialId material)
|
||||
: space(space), material(material) {}
|
||||
|
||||
inline bool MaterialSpace::operator==(MaterialSpace const& rhs) const {
|
||||
return tie(space, material) == tie(rhs.space, rhs.material);
|
||||
return space == rhs.space
|
||||
&& material == rhs.material
|
||||
&& prevCollision == rhs.prevCollision;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -76,7 +76,10 @@ public:
|
||||
virtual EntityPtr findEntityAtTile(Vec2I const& pos, EntityFilterOf<TileEntity> entityFilter) const = 0;
|
||||
|
||||
// Is the given tile layer and position occupied by an entity or block?
|
||||
virtual bool tileIsOccupied(Vec2I const& pos, TileLayer layer, bool includeEphemeral = false) const = 0;
|
||||
virtual bool tileIsOccupied(Vec2I const& pos, TileLayer layer, bool includeEphemeral = false, bool checkCollision = false) const = 0;
|
||||
|
||||
// Returns the collision kind of a tile.
|
||||
virtual CollisionKind tileCollisionKind(Vec2I const& pos) const = 0;
|
||||
|
||||
// Iterate over the collision block for each tile in the region. Collision
|
||||
// polys for tiles can extend to a maximum of 1 tile outside of the natural
|
||||
|
Loading…
Reference in New Issue
Block a user