Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/game/WorldHandlers/GridMap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,7 @@ GridMapLiquidStatus TerrainInfo::getLiquidStatus(float x, float y, float z,
const world::terrain::Column column =
ColumnAt(x, y, z + FLOOR_BURIED_LIFT, z - FLOOR_SEARCH_DOWN);

const auto liquid = column.HighestLiquid();
const auto liquid = column.LiquidFor(z);
if (!liquid || !liquid->liquidEntry)
{
return LIQUID_MAP_NO_WATER;
Expand Down
34 changes: 34 additions & 0 deletions src/shared/terrain/Column.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ namespace world::terrain
LiquidKind liquid = LiquidKind::None;
uint16_t liquidEntry = 0;
bool deep = false;
std::optional<float> terrainZ; ///< see LiquidInfo::terrainZ

bool Solid() const { return kind != SurfaceKind::Liquid; }

Expand All @@ -39,10 +40,15 @@ namespace world::terrain
info.kind = liquid;
info.entry = liquidEntry;
info.deep = deep;
info.terrainZ = terrainZ;
return info;
}
};

/// How far under its ADT terrain a point may be and still be in that tile's
/// liquid.
constexpr float ADT_LIQUID_BURIED_LIMIT = 2.0f;

class Column
{
public:
Expand All @@ -62,6 +68,7 @@ namespace world::terrain
s.liquid = info.kind;
s.liquidEntry = info.entry;
s.deep = info.deep;
s.terrainZ = info.terrainZ;
m_surfaces.push_back(s);
}

Expand Down Expand Up @@ -131,6 +138,33 @@ namespace world::terrain
return best;
}

/// The liquid a point at z is in, under or over: the highest one
/// that reaches it. An ADT liquid does not reach a point when it
/// lies under its own terrain, or the point is more than
/// ADT_LIQUID_BURIED_LIMIT under that terrain -- a cave, or a city
/// built below the surface (the Undercity under Tirisfal) -- as the
/// reference GridMap::getLiquidStatus had it.
std::optional<Surface> LiquidFor(float z) const
{
std::optional<Surface> best;
for (const Surface& s : m_surfaces)
{
if (s.kind != SurfaceKind::Liquid ||
(best && s.z <= best->z))
{
continue;
}
if (s.terrainZ &&
(s.z < *s.terrainZ ||
z < *s.terrainZ - ADT_LIQUID_BURIED_LIMIT))
{
continue;
}
best = s;
}
return best;
}

private:
std::vector<Surface> m_surfaces;
};
Expand Down
3 changes: 3 additions & 0 deletions src/shared/terrain/FusedTerrain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,9 @@ namespace world::terrain
{
if (auto adt = tile->LiquidAt(x, y))
{
// The terrain it lies on: a point buried under that terrain is
// not in it (Column::LiquidFor), however far below the floor.
adt->terrainZ = tile->TerrainHeight(x, y);
column.AddLiquid(*adt);
}
}
Expand Down
3 changes: 3 additions & 0 deletions src/shared/terrain/Terrain.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ namespace world::terrain
LiquidKind kind = LiquidKind::None;
uint16_t entry = 0; ///< LiquidType.dbc row id (0 = unknown)
bool deep = false; ///< drives swim fatigue ("dark water")
/// The ADT terrain under an ADT liquid; none for a model's liquid or
/// over a hole.
std::optional<float> terrainZ;
};

struct StaticInstance
Expand Down
75 changes: 75 additions & 0 deletions src/tests/TerrainModelTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "TestHarness.h"

#include "terrain/CollisionModel.hpp"
#include "terrain/Column.hpp"
#include "terrain/Terrain.hpp"
#include "terrain/WmoModel.hpp"

Expand Down Expand Up @@ -336,3 +337,77 @@ TEST(WmoAreaInfoNamesTheGroupTheRayHit)
REQUIRE(below.has_value());
CHECK_EQ(below->groupId, uint32_t(111));
}

namespace
{
LiquidInfo AdtWater(float level, std::optional<float> terrainZ)
{
LiquidInfo info;
info.level = level;
info.kind = LiquidKind::Water;
info.entry = 1;
info.terrainZ = terrainZ;
return info;
}
}

TEST(AdtLiquidDoesNotReachAPointFarUnderItsTerrain)
{
// The Undercity, Miles Dexter: Tirisfal's water at 32.93, under its own
// terrain at 34.14, the city's floor 95 yards down. The reference GridMap
// left it dry.
Column c;
c.AddSolid(-62.28f, SurfaceKind::Static);
c.AddLiquid(AdtWater(32.93f, 34.14f));
CHECK(!c.LiquidFor(-62.2f).has_value());
// still there for a caller that asks for any liquid
CHECK(c.HighestLiquid().has_value());
}

TEST(AdtLiquidReachesAPointAboveOrJustUnderItsTerrain)
{
Column c;
c.AddSolid(10.f, SurfaceKind::Terrain);
c.AddLiquid(AdtWater(15.f, 10.f));
const auto swimming = c.LiquidFor(12.f);
REQUIRE(swimming.has_value());
CHECK_EQ(swimming->z, 15.f);
CHECK(c.LiquidFor(10.f - ADT_LIQUID_BURIED_LIMIT + 0.5f).has_value());
CHECK(!c.LiquidFor(10.f - ADT_LIQUID_BURIED_LIMIT - 0.5f).has_value());
}

TEST(AdtLiquidUnderItsOwnTerrainIsNoLiquid)
{
Column c;
c.AddLiquid(AdtWater(32.93f, 34.14f));
CHECK(!c.LiquidFor(40.f).has_value());
}

TEST(AdtLiquidOverAHoleAndModelLiquidAreNotFiltered)
{
// No terrain under it (a hole cut for a WMO entrance) or a WMO's own
// liquid: the floor checks of the caller decide, as before.
Column c;
c.AddLiquid(AdtWater(32.93f, std::nullopt));
LiquidInfo wmo = AdtWater(-60.f, std::nullopt);
wmo.kind = LiquidKind::Slime;
c.AddLiquid(wmo);
const auto hole = c.LiquidFor(-62.2f);
REQUIRE(hole.has_value());
CHECK_EQ(hole->z, 32.93f);
}

TEST(BuriedAdtLiquidLeavesAModelLiquidBelowIt)
{
// The canal of a city under a lake: the lake is out, the canal is the
// answer.
Column c;
c.AddLiquid(AdtWater(32.93f, 30.f));
LiquidInfo canal = AdtWater(-63.f, std::nullopt);
canal.kind = LiquidKind::Slime;
c.AddLiquid(canal);
const auto liquid = c.LiquidFor(-64.f);
REQUIRE(liquid.has_value());
CHECK_EQ(liquid->z, -63.f);
CHECK(liquid->liquid == LiquidKind::Slime);
}
2 changes: 1 addition & 1 deletion src/tools/height-check/HeightCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ int main(int argc, char** argv)

if (verbose)
{
const auto liquid = column.HighestLiquid();
const auto liquid = column.LiquidFor(p.z);
char liq[80] = "none";
if (liquid)
{
Expand Down