Skip to content
Merged
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
54 changes: 54 additions & 0 deletions Core/GameEngine/Include/GameClient/TerrainRoads.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,16 @@ class TerrainRoadType : public MemoryPoolObject
Real getTransitionEffectsHeight() { return m_transitionEffectsHeight; }
Int getNumFXPerType() { return m_numFXPerType; }
Real getBridgeHoleAreaPercentage() { return m_bridgeHoleAreaPercentage; }
Bool isDestroyable( void ) { return m_isDestroyable; }
AsciiString getBridgeObjectName( void ) { return m_bridgeObjectName; }
Real getBridgeDeckHeight( void ) { return m_bridgeDeckHeight; }
UnsignedInt getBridgeCollapseDuration( void ) { return m_bridgeCollapseDuration; }
Real getBridgeCollapseDrop( void ) { return m_bridgeCollapseDrop; }
Real getBridgeCollapseTilt( void ) { return m_bridgeCollapseTilt; }
Real getBridgeCollapseStagger( void ) { return m_bridgeCollapseStagger; }
UnsignedInt getBridgeRebuildDuration( void ) { return m_bridgeRebuildDuration; }
Real getBridgeCollapseSingleSpanRoll( void ) { return m_bridgeCollapseSingleSpanRoll; }
Real getBridgeCollapseSingleSpanDrop( void ) { return m_bridgeCollapseSingleSpanDrop; }

// friend access methods to be used by the road collection only!
void friend_setName( AsciiString name ) { m_name = name; }
Expand Down Expand Up @@ -127,6 +137,18 @@ class TerrainRoadType : public MemoryPoolObject
void friend_setRepairedToFXString( BodyDamageType state, Int index, AsciiString s ) { m_repairedToFXString[ state ][ index ] = s; }
void friend_setTransitionEffectsHeight( Real height ) { m_transitionEffectsHeight = height; }
void friend_setNumFXPerType( Int num ) { m_numFXPerType = num; }
void friend_setBridgeHoleAreaPercentage( Real percentage ) { m_bridgeHoleAreaPercentage = percentage; }
void friend_setDestroyable( Bool destroyable ) { m_isDestroyable = destroyable; }
void friend_setBridgeObjectName( AsciiString name ) { m_bridgeObjectName = name; }
void friend_setBridgeDeckHeight( Real height ) { m_bridgeDeckHeight = height; }
void friend_setBridgeCollapseDuration( UnsignedInt frames ) { m_bridgeCollapseDuration = frames; }
void friend_setBridgeCollapseDrop( Real drop ) { m_bridgeCollapseDrop = drop; }
void friend_setBridgeCollapseTilt( Real tilt ) { m_bridgeCollapseTilt = tilt; }
void friend_setBridgeCollapseStagger( Real stagger ) { m_bridgeCollapseStagger = stagger; }
void friend_setBridgeRebuildDuration( UnsignedInt frames ) { m_bridgeRebuildDuration = frames; }
void friend_setBridgeCollapseSingleSpanRoll( Real roll ) { m_bridgeCollapseSingleSpanRoll = roll; }
void friend_setBridgeCollapseSingleSpanDrop( Real drop ) { m_bridgeCollapseSingleSpanDrop = drop; }
void friend_setRadarColor( RGBColor color ) { m_radarColor = color; }

/// get the parsing table for INI
const FieldParse *getRoadFieldParse() { return m_terrainRoadFieldParseTable; }
Expand Down Expand Up @@ -190,6 +212,38 @@ class TerrainRoadType : public MemoryPoolObject
Int m_numFXPerType; ///< for *each* fx/ocl we will make this many of them on the bridge area

Real m_bridgeHoleAreaPercentage; ///< if bridge is openable/destroyable, how much % of length becomes open

//
// non landmark bridges are drawn procedurally and are represented in the logic by an object
// created from m_bridgeObjectName; when destroyable they also get the 4 targetable towers
// that landmark bridges have
//
Bool m_isDestroyable; ///< true if this bridge can be destroyed
AsciiString m_bridgeObjectName; ///< object representing the bridge span in the logic

//
// the deck plane is the driving surface; a thick deck, girders or arches hang below it and
// take away room from anything passing underneath
//
Real m_bridgeDeckHeight; ///< thickness of the deck below the driving surface

//
// a sectional bridge deck is baked into a shared vertex buffer and has no skeleton, so it
// cannot play a model animation. these drive a procedural fold-and-drop of the span sections
// instead. all default to zero, which reproduces the old instant model swap exactly.
//
UnsignedInt m_bridgeCollapseDuration; ///< frames the collapse animation runs, 0 disables it
Real m_bridgeCollapseDrop; ///< world units a fully collapsed section falls
Real m_bridgeCollapseTilt; ///< radians a fully collapsed section folds by
Real m_bridgeCollapseStagger; ///< 0..1 of the duration spent rippling out from mid span
UnsignedInt m_bridgeRebuildDuration; ///< frames the rebuild animation runs, 0 disables it

//
// a bridge short enough to resolve to a single span section has nothing to fold against, so
// it banks sideways and sinks instead of hinging. a zero drop falls back to the value above.
//
Real m_bridgeCollapseSingleSpanRoll; ///< radians a one-span deck banks by, sign picks the side
Real m_bridgeCollapseSingleSpanDrop; ///< world units a one-span deck sinks, 0 means use the normal drop
};

//-------------------------------------------------------------------------------------------------
Expand Down
34 changes: 34 additions & 0 deletions Core/GameEngine/Source/GameClient/Terrain/TerrainRoads.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,16 @@ const FieldParse TerrainRoadType::m_terrainBridgeFieldParseTable[] =
{ "TransitionToOCL", parseTransitionToOCL, nullptr, 0 },
{ "TransitionToFX", parseTransitionToFX, nullptr, 0 },
{ "BridgeHoleAreaPercentage", INI::parsePercentToReal, nullptr, offsetof( TerrainRoadType, m_bridgeHoleAreaPercentage) },
{ "Destroyable", INI::parseBool, nullptr, offsetof( TerrainRoadType, m_isDestroyable ) },
{ "BridgeObjectName", INI::parseAsciiString, nullptr, offsetof( TerrainRoadType, m_bridgeObjectName ) },
{ "BridgeDeckHeight", INI::parseReal, nullptr, offsetof( TerrainRoadType, m_bridgeDeckHeight ) },
{ "BridgeCollapseDuration", INI::parseDurationUnsignedInt,nullptr, offsetof( TerrainRoadType, m_bridgeCollapseDuration ) },
{ "BridgeCollapseDrop", INI::parseReal, nullptr, offsetof( TerrainRoadType, m_bridgeCollapseDrop ) },
{ "BridgeCollapseTilt", INI::parseAngleReal, nullptr, offsetof( TerrainRoadType, m_bridgeCollapseTilt ) },
{ "BridgeCollapseStagger", INI::parsePercentToReal, nullptr, offsetof( TerrainRoadType, m_bridgeCollapseStagger ) },
{ "BridgeRebuildDuration", INI::parseDurationUnsignedInt,nullptr, offsetof( TerrainRoadType, m_bridgeRebuildDuration ) },
{ "BridgeCollapseSingleSpanRoll", INI::parseAngleReal, nullptr, offsetof( TerrainRoadType, m_bridgeCollapseSingleSpanRoll ) },
{ "BridgeCollapseSingleSpanDrop", INI::parseReal, nullptr, offsetof( TerrainRoadType, m_bridgeCollapseSingleSpanDrop ) },


{ nullptr, nullptr, nullptr, 0 },
Expand Down Expand Up @@ -219,6 +229,16 @@ TerrainRoadType::TerrainRoadType()
m_radarColor.blue = 0.0f;
m_transitionEffectsHeight = 0.0f;
m_numFXPerType = 0;
m_bridgeHoleAreaPercentage = 0.0f;
m_isDestroyable = FALSE;
m_bridgeDeckHeight = 0.0f;
m_bridgeCollapseDuration = 0;
m_bridgeCollapseDrop = 0.0f;
m_bridgeCollapseTilt = 0.0f;
m_bridgeCollapseStagger = 0.0f;
m_bridgeRebuildDuration = 0;
m_bridgeCollapseSingleSpanRoll = 0.0f;
m_bridgeCollapseSingleSpanDrop = 0.0f;

}

Expand Down Expand Up @@ -406,6 +426,20 @@ TerrainRoadType *TerrainRoadCollection::newBridge( AsciiString name )

bridge->friend_setTransitionEffectsHeight( defaultBridge->getTransitionEffectsHeight() );
bridge->friend_setNumFXPerType( defaultBridge->getNumFXPerType() );
bridge->friend_setBridgeHoleAreaPercentage( defaultBridge->getBridgeHoleAreaPercentage() );
bridge->friend_setDestroyable( defaultBridge->isDestroyable() );
bridge->friend_setBridgeObjectName( defaultBridge->getBridgeObjectName() );
bridge->friend_setBridgeDeckHeight( defaultBridge->getBridgeDeckHeight() );
bridge->friend_setBridgeCollapseDuration( defaultBridge->getBridgeCollapseDuration() );
bridge->friend_setBridgeCollapseDrop( defaultBridge->getBridgeCollapseDrop() );
bridge->friend_setBridgeCollapseTilt( defaultBridge->getBridgeCollapseTilt() );
bridge->friend_setBridgeCollapseStagger( defaultBridge->getBridgeCollapseStagger() );
bridge->friend_setBridgeRebuildDuration( defaultBridge->getBridgeRebuildDuration() );
bridge->friend_setBridgeCollapseSingleSpanRoll( defaultBridge->getBridgeCollapseSingleSpanRoll() );
bridge->friend_setBridgeCollapseSingleSpanDrop( defaultBridge->getBridgeCollapseSingleSpanDrop() );

// a block that omits RadarColor drew black on the radar without this
bridge->friend_setRadarColor( defaultBridge->getRadarColor() );
for( Int state = BODY_PRISTINE; state < BODYDAMAGETYPE_COUNT; state++ )
{

Expand Down
18 changes: 16 additions & 2 deletions Core/GameEngine/Source/GameLogic/AI/AIPathfind.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
#include "Common/ThingFactory.h"

#include "GameClient/Line2D.h"
#include "GameClient/TerrainRoads.h"

#include "GameLogic/AI.h"
#include "GameLogic/GameLogic.h"
Expand Down Expand Up @@ -4369,6 +4370,12 @@ void Pathfinder::classifyObjectFootprint( Object *obj, Bool insert )
return; // It is important to not abuse bridge towers.
}

if (obj->isKindOf(KINDOF_BRIDGE) && !obj->getTemplate()->isBridge()) {
// Procedural bridge span. Its deck is a pathfind layer of its own, and the ground below it
// is governed by the bridge clearance, not by this object's placeholder box.
return;
}

if (obj->getTemplate()->getFenceWidth() > 0.0f)
{
if (!obj->isKindOf(KINDOF_DEFENSIVE_WALL))
Expand Down Expand Up @@ -4861,6 +4868,10 @@ static void calculateBridgeHeights(IRegion2D bounds, PathfindCell** map)
if (cellHiX > bounds.hi.x) cellHiX = bounds.hi.x;
if (cellHiY > bounds.hi.y) cellHiY = bounds.hi.y;

// a thick deck, girders or arches hang below the driving surface and take away room
TerrainRoadType *bridgeTemplate = TheTerrainRoads->findBridge( bridge->getBridgeTemplateName() );
Real deckHeight = bridgeTemplate ? bridgeTemplate->getBridgeDeckHeight() : 0.0f;

for (Int i = cellLoX; i < cellHiX; ++i) {
for (Int j = cellLoY; j < cellHiY; ++j) {
Real worldX = ((Real)i + 0.5f) * PATHFIND_CELL_SIZE_F;
Expand All @@ -4878,12 +4889,15 @@ static void calculateBridgeHeights(IRegion2D bounds, PathfindCell** map)
continue;
}

// the deck plane through the two bridge points is what a unit passing below clears
Real bridgeZ = TheTerrainLogic->getLayerHeight(worldX, worldY, layer);
Real waterZ, groundZ;

// isUnderwater leaves waterZ untouched where there is no water, so seed both
Real waterZ = 0.0f, groundZ = 0.0f;
TheTerrainLogic->isUnderwater(worldX, worldY, &waterZ, &groundZ);
Real baseZ = (waterZ > groundZ) ? waterZ : groundZ;

Real gap = bridgeZ - baseZ;
Real gap = bridgeZ - deckHeight - baseZ;
if (gap < 0.0f) gap = 0.0f;

Int encoded = (Int)(gap / 10.0f);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ struct TimeAndLocationInfo
{
UnsignedInt delay; ///< how long to wait to execute this
AsciiString boneName; ///< which bone to execute at
Real spanFraction; ///< 0..1 along the bridge centreline, <0 means unset
};
// ------------------------------------------------------------------------------------------------
struct BridgeFXInfo
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ class Bridge : public MemoryPoolObject
Bool hasHoleArea(); // check if this bridge has defined a hole area for damaged/drawbridge state
Bool hasHole(); // Check if bridge currently has a hole (destroyed/drawbridge open)
void setDrawBridgeStage(bool open); // change if bridge is open/closed
void updateBridgeObjectGeometry(); // size the bridge object's collision box, or collapse it when the deck is gone
};

//-------------------------------------------------------------------------------------------------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1076,7 +1076,8 @@ void ThingTemplate::parseRequiredBridgeHeight(INI* ini, void* instance, void* st
self->m_requiredBridgeHeight = -1;
}
else {
self->m_requiredBridgeHeight = std::clamp(static_cast<byte>(value / 10.0f), static_cast<byte>(0), static_cast<byte>(15));
// rounded up, see Object::getRequiredBridgeHeight. 0 still means "never blocked".
self->m_requiredBridgeHeight = std::clamp(static_cast<byte>(REAL_TO_INT_CEIL(value / 10.0f)), static_cast<byte>(0), static_cast<byte>(15));
}
}

Expand Down
Loading
Loading