diff --git a/Core/GameEngineDevice/Include/W3DDevice/GameClient/BaseHeightMap.h b/Core/GameEngineDevice/Include/W3DDevice/GameClient/BaseHeightMap.h index 9d3469a36b1..48d3be58b53 100644 --- a/Core/GameEngineDevice/Include/W3DDevice/GameClient/BaseHeightMap.h +++ b/Core/GameEngineDevice/Include/W3DDevice/GameClient/BaseHeightMap.h @@ -37,7 +37,6 @@ #include "W3DDevice/GameClient/WorldHeightMap.h" #define MAX_ENABLED_DYNAMIC_LIGHTS 20 -typedef UnsignedByte HeightSampleType; //type of data to store in heightmap class W3DTreeBuffer; class W3DBibBuffer; class W3DRoadBuffer; @@ -133,7 +132,7 @@ class BaseHeightMapRenderObjClass : public RenderObjClass, public DX8_CleanupHoo } - UnsignedByte getClipHeight(Int x, Int y) const + HeightSampleType getClipHeight(Int x, Int y) const { Int xextent = m_map->getXExtent() - 1; Int yextent = m_map->getYExtent() - 1; diff --git a/Core/GameEngineDevice/Include/W3DDevice/GameClient/WorldHeightMap.h b/Core/GameEngineDevice/Include/W3DDevice/GameClient/WorldHeightMap.h index 932edf2a7a5..bba3bac0d1c 100644 --- a/Core/GameEngineDevice/Include/W3DDevice/GameClient/WorldHeightMap.h +++ b/Core/GameEngineDevice/Include/W3DDevice/GameClient/WorldHeightMap.h @@ -37,7 +37,7 @@ #include "Common/STLTypedefs.h" typedef std::vector VecICoord2D; - +typedef UnsignedShort HeightSampleType; //type of data to store in heightmap, changed to short (2bytes) to allow more height difference /** MapObject class Not ref counted. Do not store pointers to this class. */ @@ -117,7 +117,7 @@ class WorldHeightMap : public RefCountClass, Int m_borderSize; ///< Non-playable border area. VecICoord2D m_boundaries; ///< the in-game boundaries Int m_dataSize; ///< size of m_data. - UnsignedByte *m_data; ///< array of z(height) values in the height map. + HeightSampleType *m_data; ///< array of z(height) values in the height map. UnsignedByte *m_seismicUpdateFlag; ///< array of bits to prevent ovelapping physics-update regions from doubling effects on shared cells UnsignedInt m_seismicUpdateWidth; ///< width of the array holding SeismicUpdateFlags @@ -223,7 +223,7 @@ class WorldHeightMap : public RefCountClass, static Int getMinHeightValue(void) {return K_MIN_HEIGHT;} static Int getMaxHeightValue(void) {return K_MAX_HEIGHT;} - UnsignedByte *getDataPtr(void) {return m_data;} + HeightSampleType *getDataPtr(void) {return m_data;} Int getXExtent(void) {return m_width;} ///=0) && (ndx=0) && (ndxgetDataPtr(); + const HeightSampleType* data = logicHeightMap->getDataPtr(); int idx = ix + iy*xExtent; float p0 = data[idx]; float p2 = data[idx + xExtent + 1]; @@ -1074,7 +1074,7 @@ Bool BaseHeightMapRenderObjClass::isClearLineOfSight(const Coord3D& pos, const C Real zinc = dz * nsInv; Bool result = true; - const UnsignedByte* data = logicHeightMap->getDataPtr(); + const HeightSampleType* data = logicHeightMap->getDataPtr(); Int xExtent = logicHeightMap->getXExtent(); Int yExtent = logicHeightMap->getYExtent(); for (Int curpixel = 0; curpixel < numpixels; curpixel++) @@ -1221,7 +1221,7 @@ Real BaseHeightMapRenderObjClass::getMaxCellHeight(Real x, Real y) const if (iY >= (logicHeightMap->getYExtent()-1)) { iY = logicHeightMap->getYExtent()-2; } - UnsignedByte *data = logicHeightMap->getDataPtr(); + HeightSampleType *data = logicHeightMap->getDataPtr(); p0=data[iX+iY*logicHeightMap->getXExtent()]*MAP_HEIGHT_SCALE; p1=data[(iX+offset)+iY*logicHeightMap->getXExtent()]*MAP_HEIGHT_SCALE; p2=data[(iX+offset)+(iY+offset)*logicHeightMap->getXExtent()]*MAP_HEIGHT_SCALE; diff --git a/Core/GameEngineDevice/Source/W3DDevice/GameClient/HeightMap.cpp b/Core/GameEngineDevice/Source/W3DDevice/GameClient/HeightMap.cpp index 55f228a81d2..62cb4accf1c 100644 --- a/Core/GameEngineDevice/Source/W3DDevice/GameClient/HeightMap.cpp +++ b/Core/GameEngineDevice/Source/W3DDevice/GameClient/HeightMap.cpp @@ -2264,7 +2264,7 @@ void HeightMapRenderObjClass::renderExtraBlendTiles(void) if (!vb || !ib) return; - const UnsignedByte* data = m_map->getDataPtr(); + const HeightSampleType* data = m_map->getDataPtr(); //Loop over visible terrain and extract all the tiles that need extra blend Int drawEdgeY=m_map->getDrawOrgY()+m_map->getDrawHeight()-1; diff --git a/Core/GameEngineDevice/Source/W3DDevice/GameClient/W3DTerrainVisual.cpp b/Core/GameEngineDevice/Source/W3DDevice/GameClient/W3DTerrainVisual.cpp index 77935049c58..770315f0f40 100644 --- a/Core/GameEngineDevice/Source/W3DDevice/GameClient/W3DTerrainVisual.cpp +++ b/Core/GameEngineDevice/Source/W3DDevice/GameClient/W3DTerrainVisual.cpp @@ -1210,8 +1210,8 @@ void W3DTerrainVisual::xfer( Xfer *xfer ) // Write out the terrain height data. if (version >= 2) { - UnsignedByte *data = m_logicHeightMap->getDataPtr(); - Int len = m_logicHeightMap->getXExtent()*m_logicHeightMap->getYExtent(); + HeightSampleType *data = m_logicHeightMap->getDataPtr(); + Int len = m_logicHeightMap->getXExtent()*m_logicHeightMap->getYExtent() * sizeof(HeightSampleType); Int xferLen = len; xfer->xferInt(&xferLen); if (len!=xferLen) { diff --git a/Core/GameEngineDevice/Source/W3DDevice/GameClient/WorldHeightMap.cpp b/Core/GameEngineDevice/Source/W3DDevice/GameClient/WorldHeightMap.cpp index 52384dfed58..225c8da6126 100644 --- a/Core/GameEngineDevice/Source/W3DDevice/GameClient/WorldHeightMap.cpp +++ b/Core/GameEngineDevice/Source/W3DDevice/GameClient/WorldHeightMap.cpp @@ -41,6 +41,7 @@ #include "Common/ThingFactory.h" #include "Common/ThingTemplate.h" #include "Common/WellKnownKeys.h" +#include "Common/MapData.h" #include "GameLogic/PolygonTrigger.h" #include "GameLogic/SidesList.h" @@ -878,7 +879,7 @@ Bool WorldHeightMap::ParseHeightMapData(DataChunkInput &file, DataChunkInfo *inf } m_dataSize = file.readInt(); - m_data = MSGNEW("WorldHeightMap_ParseHeightMapData") UnsignedByte[m_dataSize]; + m_data = MSGNEW("WorldHeightMap_ParseHeightMapData") HeightSampleType[m_dataSize]; if (m_dataSize <= 0 || (m_dataSize != (m_width*m_height))) { throw ERROR_CORRUPT_FILE_FORMAT ; } @@ -891,8 +892,14 @@ Bool WorldHeightMap::ParseHeightMapData(DataChunkInput &file, DataChunkInfo *inf m_seismicZVelocities = MSGNEW("WorldHeightMap_ParseHeightMapData _ zvelocities allocated") Real[m_dataSize]; fillSeismicZVelocities( 0 ); + //load as bytes first + std::vector loaded_data(m_dataSize); + + file.readArrayOfBytes((char *)&loaded_data.at(0), m_dataSize); + for (size_t i = 0; i < loaded_data.size(); i++) { + m_data[i] = static_cast(std::round(loaded_data[i]* TheMapData->m_HeightmapScale)); + } - file.readArrayOfBytes((char *)m_data, m_dataSize); // Resize me. if (info->version == K_HEIGHT_MAP_VERSION_1) { Int newWidth = (m_width+1)/2; @@ -952,11 +959,18 @@ Bool WorldHeightMap::ParseSizeOnly(DataChunkInput &file, DataChunkInfo *info, vo } m_dataSize = file.readInt(); - m_data = MSGNEW("WorldHeightMap_ParseSizeOnly") UnsignedByte[m_dataSize]; + m_data = MSGNEW("WorldHeightMap_ParseSizeOnly") HeightSampleType[m_dataSize]; if (m_dataSize <= 0 || (m_dataSize != (m_width*m_height))) { throw ERROR_CORRUPT_FILE_FORMAT ; } - file.readArrayOfBytes((char *)m_data, m_dataSize); + + //load as bytes first + std::vector loaded_data(m_dataSize); + file.readArrayOfBytes((char*)&loaded_data.at(0), m_dataSize); + for (size_t i = 0; i < loaded_data.size(); i++) { + m_data[i] = static_cast(std::round(loaded_data[i] * TheMapData->m_HeightmapScale)); + } + // Resize me. if (info->version == K_HEIGHT_MAP_VERSION_1) { Int newWidth = (m_width+1)/2; diff --git a/Generals/Code/Tools/WorldBuilder/src/FeatherTool.cpp b/Generals/Code/Tools/WorldBuilder/src/FeatherTool.cpp index 117004025ff..3b73e70f48b 100644 --- a/Generals/Code/Tools/WorldBuilder/src/FeatherTool.cpp +++ b/Generals/Code/Tools/WorldBuilder/src/FeatherTool.cpp @@ -110,7 +110,7 @@ void FeatherTool::mouseDown(TTrackingMode m, CPoint viewPt, WbView* pView, CWorl m_htMapFeatherCopy = pDoc->GetHeightMap()->duplicate(); m_htMapRateCopy = pDoc->GetHeightMap()->duplicate(); Int size = m_htMapRateCopy->getXExtent() * m_htMapRateCopy->getYExtent(); - UnsignedByte *pData = m_htMapRateCopy->getDataPtr(); + HeightSampleType *pData = m_htMapRateCopy->getDataPtr(); Int i; for (i=0; iupdateHeightMap(m_htMapEditCopy, true, partialRange); if (redoRate) { Int size = m_htMapRateCopy->getXExtent() * m_htMapRateCopy->getYExtent(); - UnsignedByte *pData = m_htMapRateCopy->getDataPtr(); - UnsignedByte *pFeather = m_htMapFeatherCopy->getDataPtr(); - UnsignedByte *pEdit = m_htMapEditCopy->getDataPtr(); + HeightSampleType *pData = m_htMapRateCopy->getDataPtr(); + HeightSampleType *pFeather = m_htMapFeatherCopy->getDataPtr(); + HeightSampleType *pEdit = m_htMapEditCopy->getDataPtr(); Int i; for (i=0; i(m_maxPathfindingCellRadius); }; + protected: // @@ -823,7 +825,7 @@ class ThingTemplate : public Overridable UnsignedByte m_crusherLevel; ///< crusher > crushable level to actually crush UnsignedByte m_crushableLevel; ///< Specifies the level of crushability (must be hit by a crusher greater than this to crush me). Byte m_ammoPipsStyle; ///< How ammo pips are displayed for this thing - + UnsignedByte m_maxPathfindingCellRadius; ///< Limit cells radius for pathfinding, defaults to 2, can be increased for large units }; //----------------------------------------------------------------------------- diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/Locomotor.h b/GeneralsMD/Code/GameEngine/Include/GameLogic/Locomotor.h index 0fb97be20a6..4bb6ca882cf 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameLogic/Locomotor.h +++ b/GeneralsMD/Code/GameEngine/Include/GameLogic/Locomotor.h @@ -101,6 +101,7 @@ enum LocomotorBehaviorZ CPP_11(: Int) Z_FIXED_ABSOLUTE_HEIGHT, // stays fixed at absolute height, regardless of physics Z_RELATIVE_TO_GROUND_AND_BUILDINGS, // stays fixed at surface-rel height including buildings, regardless of physics Z_SMOOTH_RELATIVE_TO_HIGHEST_LAYER, // try to follow a height relative to the highest layer. + Z_SEA_SURFACE_RELATIVE_HEIGHT, //try to follow a specific height relative to terrain / water height, optimized for submarines LOCOMOTOR_BEHAVIOR_Z_COUNT }; @@ -116,6 +117,7 @@ static const char *const TheLocomotorBehaviorZNames[] = "FIXED_ABSOLUTE_HEIGHT", "FIXED_RELATIVE_TO_GROUND_AND_BUILDINGS", "RELATIVE_TO_HIGHEST_LAYER", + "SEA_SURFACE_RELATIVE_HEIGHT", NULL }; diff --git a/GeneralsMD/Code/GameEngine/Source/Common/GameEngine.cpp b/GeneralsMD/Code/GameEngine/Source/Common/GameEngine.cpp index 3ce34e9ecb2..fc1516a7147 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/GameEngine.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/GameEngine.cpp @@ -71,6 +71,7 @@ #include "Common/GameLOD.h" #include "Common/Registry.h" #include "Common/GameCommon.h" // FOR THE ALLOW_DEBUG_CHEATS_IN_RELEASE #define +#include "Common/MapData.h" #include "GameLogic/Armor.h" #include "GameLogic/AI.h" @@ -451,6 +452,7 @@ void GameEngine::init() initSubsystem(TheWritableGlobalData, "TheWritableGlobalData", TheWritableGlobalData, &xferCRC, "Data\\INI\\Default\\GameData", "Data\\INI\\GameData"); TheWritableGlobalData->parseCustomDefinition(); + initSubsystem(TheWriteableMapData, "TheWriteableMapData", MapData::createMapDataSystem(), &xferCRC); #ifdef DUMP_PERF_STATS/////////////////////////////////////////////////////////////////////////// GetPrecisionTimer(&endTime64);////////////////////////////////////////////////////////////////// diff --git a/GeneralsMD/Code/GameEngine/Source/Common/INI/INI.cpp b/GeneralsMD/Code/GameEngine/Source/Common/INI/INI.cpp index 96bd2eb0846..1e144647c03 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/INI/INI.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/INI/INI.cpp @@ -420,7 +420,11 @@ UnsignedInt INI::load( AsciiString filename, INILoadType loadType, Xfer *pXfer, // the first word is the type of data we're processing const char *token = strtok( m_buffer, m_seps ); - if( token ) + + // skip non MapData blocks if loading + bool skip = (loadType == INI_LOAD_MAPDATA_ONLY) && (strcmp(token, "MapData") != 0); + + if( token && !skip) { INIBlockParse parse = findBlockParse(token); if (parse) diff --git a/GeneralsMD/Code/GameEngine/Source/Common/INI/INIMapData.cpp b/GeneralsMD/Code/GameEngine/Source/Common/INI/INIMapData.cpp index 16632e27920..46b208e700e 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/INI/INIMapData.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/INI/INIMapData.cpp @@ -31,6 +31,7 @@ #include "PreRTS.h" // This must go first in EVERY cpp file in the GameEngine #include "Common/INI.h" +#include "Common/MapData.h" /////////////////////////////////////////////////////////////////////////////////////////////////// // PRIVATE DATA /////////////////////////////////////////////////////////////////////////////////// @@ -45,6 +46,7 @@ //------------------------------------------------------------------------------------------------- void INI::parseMapDataDefinition( INI* ini ) { + MapData::parseMapDataDefinition(ini); } diff --git a/GeneralsMD/Code/GameEngine/Source/Common/MapData.cpp b/GeneralsMD/Code/GameEngine/Source/Common/MapData.cpp new file mode 100644 index 00000000000..01114cd4643 --- /dev/null +++ b/GeneralsMD/Code/GameEngine/Source/Common/MapData.cpp @@ -0,0 +1,43 @@ +////////////////////////////////////////// +// FILE: MapData.cpp +// Store some extra metadata about the current map +/////////////////////////////////////////////////////////////////////////////////////////////////// + +// INCLUDES /////////////////////////////////////////////////////////////////////////////////////// +#include "PreRTS.h" // This must go first in EVERY cpp file in the GameEngine + +#include "Common/MapData.h" + +// PUBLIC DATA //////////////////////////////////////////////////////////////////////////////////// +MapData* TheWriteableMapData = NULL; ///< The current map data singleton + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// PRIVATE DATA /////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////// +/*static*/ const FieldParse MapData::s_MapDataFieldParseTable[] = +{ + { "HeightMapScale", INI::parseReal, NULL, offsetof( MapData, m_HeightmapScale) }, + { "EnableShips", INI::parseBool, NULL, offsetof( MapData, m_enableShips) }, + { NULL, NULL, NULL, 0 } // keep this last + +}; + +MapData::MapData() : SubsystemInterface() +{ + m_HeightmapScale = 1.0f; + m_enableShips = false; +} + +void MapData::init() { + m_HeightmapScale = 1.0f; + m_enableShips = false; +} + +void MapData::reset() { + m_HeightmapScale = 1.0f; + m_enableShips = false; +} + +void MapData::parseMapDataDefinition(INI* ini) { + ini->initFromINI(TheWriteableMapData, s_MapDataFieldParseTable); +} diff --git a/GeneralsMD/Code/GameEngine/Source/Common/System/BuildAssistant.cpp b/GeneralsMD/Code/GameEngine/Source/Common/System/BuildAssistant.cpp index 635c0811fa2..b26dca14b1e 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/System/BuildAssistant.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/System/BuildAssistant.cpp @@ -1195,8 +1195,9 @@ LegalBuildCode BuildAssistant::isLocationLegalToBuild( const Coord3D *worldPos, Short totalSamples = sampleData.waterSamples + sampleData.landSamples; Real threshold_water = totalSamples * 0.6f; Real threshold_land = 1.0f; + Real max_land = totalSamples * 0.2f; - if (static_cast(sampleData.waterSamples) < threshold_water || static_cast(sampleData.landSamples) < threshold_land) { + if (static_cast(sampleData.waterSamples) < threshold_water || static_cast(sampleData.landSamples) < threshold_land || static_cast(sampleData.landSamples) > max_land) { return LBC_RESTRICTED_TERRAIN; } diff --git a/GeneralsMD/Code/GameEngine/Source/Common/Thing/ThingTemplate.cpp b/GeneralsMD/Code/GameEngine/Source/Common/Thing/ThingTemplate.cpp index 355c00723f0..5b8f72d8e13 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/Thing/ThingTemplate.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/Thing/ThingTemplate.cpp @@ -250,6 +250,7 @@ const FieldParse ThingTemplate::s_objectFieldParseTable[] = { "CrusherLevel", INI::parseUnsignedByte, NULL, offsetof( ThingTemplate, m_crusherLevel ) }, { "CrushableLevel", INI::parseUnsignedByte, NULL, offsetof( ThingTemplate, m_crushableLevel ) }, { "AmmoPipsStyle", INI::parseByteSizedIndexList, AmmoPipsStyleNames, offsetof(ThingTemplate, m_ammoPipsStyle) }, + { "MaxPathfindingCellRadius", INI::parseUnsignedByte, NULL, offsetof(ThingTemplate, m_maxPathfindingCellRadius) }, { 0, 0, 0, 0 } }; @@ -1048,6 +1049,7 @@ ThingTemplate::ThingTemplate() : m_crushableLevel = 255; //Unspecified, this object is unable to be crushed by anything! m_ammoPipsStyle = AMMO_PIPS_DEFAULT; + m_maxPathfindingCellRadius = 2U; } //------------------------------------------------------------------------------------------------- diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/ControlBar/ControlBar.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/ControlBar/ControlBar.cpp index e097cba0d25..a65717ba145 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/ControlBar/ControlBar.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/ControlBar/ControlBar.cpp @@ -59,6 +59,7 @@ #include "GameLogic/Module/StealthUpdate.h" #include "GameLogic/Module/RebuildHoleBehavior.h" #include "GameLogic/ScriptEngine.h" +#include "GameLogic/TerrainLogic.h" #include "GameClient/AnimateWindowManager.h" #include "GameClient/ControlBar.h" @@ -2764,8 +2765,17 @@ void ControlBar::showRallyPoint(const Coord3D* loc) // sanity DEBUG_ASSERTCRASH(marker, ("showRallyPoint: No rally point marker found")); - // set the position of the rally point drawble to the position passed in - marker->setPosition(loc); + // Adapt position to water height if under water + Real waterZ{ 0 }; + if (TheTerrainLogic->isUnderwater(loc->x, loc->y, &waterZ)) { + Coord3D waterLoc = *loc; + waterLoc.z = waterZ; + marker->setPosition(&waterLoc); + } + else { + // set the position of the rally point drawble to the position passed in + marker->setPosition(loc); + } marker->setOrientation(TheGlobalData->m_downwindAngle); // To blow down wind -- ML // set the marker colors to that of the local player diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/InGameUI.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/InGameUI.cpp index ee6b325d4b9..bcd799a13bd 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/InGameUI.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/InGameUI.cpp @@ -1581,6 +1581,11 @@ void InGameUI::handleBuildPlacements( void ) Coord3D worldPos; TheTacticalView->screenToTerrain(&loc, &worldPos); + Real terrainZ{ 0 }; + Real waterZ{ 0 }; + TheTerrainLogic->isUnderwater(worldPos.x, worldPos.y, &waterZ, &terrainZ); + worldPos.z = std::max(terrainZ, waterZ); + Real check_radius = 0.0f; if (geom.getGeomType() == GEOMETRY_BOX) { @@ -1626,6 +1631,15 @@ void InGameUI::handleBuildPlacements( void ) /**@todo this whole orientation vector thing is LAME! Must replace, all I want to to do is set a simple angle and have it automatically change, ug! */ TheTacticalView->screenToTerrain( &loc, &world ); + + // If shipyard move up building to at least waterheight if lower + if (m_pendingPlaceType->isKindOf(KINDOF_SHIPYARD)) { + Real terrainZ{ 0 }; + Real waterZ{ 0 }; + TheTerrainLogic->isUnderwater(world.x, world.y, &waterZ, &terrainZ); + world.z = std::max(terrainZ, waterZ); + } + m_placeIcon[ 0 ]->setPosition( &world ); m_placeIcon[ 0 ]->setOrientation( angle ); diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/AI/AIPathfind.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/AI/AIPathfind.cpp index 6040e59d77c..e50c5cf5fc2 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/AI/AIPathfind.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/AI/AIPathfind.cpp @@ -9650,13 +9650,14 @@ void Pathfinder::changeBridgeState( PathfindLayerEnum layer, Bool repaired) void Pathfinder::getRadiusAndCenter(const Object *obj, Int &iRadius, Bool ¢er) { - enum {MAX_RADIUS = 2}; if (!obj) { center = true; iRadius = 0; return; } + const Int max_radius = obj->getTemplate() != nullptr ? obj->getTemplate()->getMaxPathFindingCellRadius() : 2; + Real diameter = 2*obj->getGeometryInfo().getBoundingCircleRadius(); if (diameter>PATHFIND_CELL_SIZE_F && diameter<2.0f*PATHFIND_CELL_SIZE_F) { diameter = 2.0f*PATHFIND_CELL_SIZE_F; @@ -9669,9 +9670,9 @@ void Pathfinder::getRadiusAndCenter(const Object *obj, Int &iRadius, Bool ¢e center = true; } iRadius /= 2; - if (iRadius > MAX_RADIUS) + if (iRadius > max_radius) { - iRadius = MAX_RADIUS; + iRadius = max_radius; center = true; } } diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Locomotor.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Locomotor.cpp index 4d9f5cefcaf..34b22f012ca 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Locomotor.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Locomotor.cpp @@ -1107,6 +1107,8 @@ void Locomotor::locoUpdate_moveTowardsPosition(Object* obj, const Coord3D& goalP moveTowardsPositionTreads(obj, physics, goalPos, onPathDistToGoal, desiredSpeed); break; case LOCO_SHIP: + moveTowardsPositionTreads(obj, physics, goalPos, onPathDistToGoal, desiredSpeed); + break; case LOCO_HOVER: moveTowardsPositionHover(obj, physics, goalPos, onPathDistToGoal, desiredSpeed); break; @@ -2377,6 +2379,47 @@ Bool Locomotor::handleBehaviorZ(Object* obj, PhysicsBehavior *physics, const Coo } } break; + + case Z_SEA_SURFACE_RELATIVE_HEIGHT: + requiresConstantCalling = TRUE; + { + if (m_preferredHeight != 0.0f || getFlag(PRECISE_Z_POS)) + { + Coord3D pos = *obj->getPosition(); + + Real waterZ; + Real terrainZ; + Real preferredHeight; + if (TheTerrainLogic->isUnderwater(pos.x, pos.y, &waterZ, &terrainZ)) { + // for submarines with negative preferred height, if water is too shallow always stay above terrain height + preferredHeight = std::max(waterZ + m_preferredHeight, terrainZ); + } + else { + preferredHeight = TheTerrainLogic->getLayerHeight(pos.x, pos.y, obj->getLayer()) + m_preferredHeight; + } + + if (getFlag(PRECISE_Z_POS)) + preferredHeight = goalPos.z; + + Real delta = preferredHeight - pos.z; + delta *= getPreferredHeightDamping(); + preferredHeight = pos.z + delta; + + // surfaceHt is an unused parameter + Real liftToUse = calcLiftToUseAtPt(obj, physics, pos.z, 0.0f, preferredHeight); + + //DEBUG_LOG(("HandleBZ %d LiftToUse %f",TheGameLogic->getFrame(),liftToUse)); + if (liftToUse != 0.0f) + { + Coord3D force; + force.x = 0.0f; + force.y = 0.0f; + force.z = liftToUse * physics->getMass(); + physics->applyMotiveForce(&force); + } + } + } + break; } return requiresConstantCalling; diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate.cpp index 70112eb1f26..e10d5e84631 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate.cpp @@ -2488,10 +2488,6 @@ Bool AIUpdateInterface::isAircraftThatAdjustsDestination(void) const { return FALSE; // thrust doesn't adjust. } - if (m_curLocomotor->getAppearance() == LOCO_SHIP) - { - return TRUE; // behave like hover - } return FALSE; } diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp index a6001e2206b..6d2f72021cc 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp @@ -62,6 +62,7 @@ #include "Common/XferCRC.h" #include "Common/XferDeepCRC.h" #include "Common/GameSpyMiscPreferences.h" +#include "Common/MapData.h" #include "GameClient/ControlBar.h" #include "GameClient/Drawable.h" @@ -2451,6 +2452,7 @@ static void findAndSelectCommandCenter(Object *obj, void* alreadyFound) // ------------------------------------------------------------------------------------------------ void GameLogic::loadMapINI( AsciiString mapName ) { + TheWriteableMapData->reset(); if (!TheMapCache) { // Need the map cache to get the map and user map directories. diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp index 699c9bba7a3..317c64895c1 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp @@ -147,7 +147,14 @@ static void doSetRallyPoint( Object *obj, const Coord3D& pos ) // for now, just use the basic human locomotor ... and enable the above code when Steven // tells me how to get the locomotor sets based on a thing template (CBD) // - NameKeyType key = NAMEKEY( "BasicHumanLocomotor" ); + NameKeyType key; + if (obj->isKindOf(KINDOF_SHIPYARD)) { + key = NAMEKEY("BasicBoatLocomotor"); + } + else { + key = NAMEKEY("BasicHumanLocomotor"); + } + LocomotorSet locomotorSet; locomotorSet.addLocomotor( TheLocomotorStore->findLocomotorTemplate( key ) ); if( TheAI->pathfinder()->clientSafeQuickDoesPathExist( locomotorSet, obj->getPosition(), &pos ) == FALSE ) diff --git a/GeneralsMD/Code/Tools/WorldBuilder/src/FeatherTool.cpp b/GeneralsMD/Code/Tools/WorldBuilder/src/FeatherTool.cpp index b50fbd0d40e..5035177d962 100644 --- a/GeneralsMD/Code/Tools/WorldBuilder/src/FeatherTool.cpp +++ b/GeneralsMD/Code/Tools/WorldBuilder/src/FeatherTool.cpp @@ -110,7 +110,7 @@ void FeatherTool::mouseDown(TTrackingMode m, CPoint viewPt, WbView* pView, CWorl m_htMapFeatherCopy = pDoc->GetHeightMap()->duplicate(); m_htMapRateCopy = pDoc->GetHeightMap()->duplicate(); Int size = m_htMapRateCopy->getXExtent() * m_htMapRateCopy->getYExtent(); - UnsignedByte *pData = m_htMapRateCopy->getDataPtr(); + HeightSampleType *pData = m_htMapRateCopy->getDataPtr(); Int i; for (i=0; iupdateHeightMap(m_htMapEditCopy, true, partialRange); if (redoRate) { Int size = m_htMapRateCopy->getXExtent() * m_htMapRateCopy->getYExtent(); - UnsignedByte *pData = m_htMapRateCopy->getDataPtr(); - UnsignedByte *pFeather = m_htMapFeatherCopy->getDataPtr(); - UnsignedByte *pEdit = m_htMapEditCopy->getDataPtr(); + HeightSampleType *pData = m_htMapRateCopy->getDataPtr(); + HeightSampleType *pFeather = m_htMapFeatherCopy->getDataPtr(); + HeightSampleType *pEdit = m_htMapEditCopy->getDataPtr(); Int i; for (i=0; i binary_data(m_dataSize); + const Real f = 1.0f / TheMapData->m_HeightmapScale; + for (size_t i = 0; i < m_dataSize; i++) { + binary_data[i] = std::clamp(static_cast(std::round(m_data[i] * f)), static_cast(0U), static_cast(255U)); + } + + chunkWriter.writeArrayOfBytes((char *)&binary_data[0], m_dataSize); /* chunkWriter.writeInt(m_width); @@ -1799,7 +1808,7 @@ Bool WorldHeightMapEdit::resize(Int newXSize, Int newYSize, Int newHeight, Int n Short *tileNdxes = new Short[newDataSize]; Short *blendTileNdxes = new Short[newDataSize]; Short *extraBlendTileNdxes = new Short[newDataSize]; - UnsignedByte *data = new UnsignedByte[newDataSize]; + HeightSampleType *data = new HeightSampleType[newDataSize]; Short *cliffInfoNdxes = new Short[newDataSize]; Int i, j; diff --git a/GeneralsMD/Code/Tools/WorldBuilder/src/WorldBuilder.cpp b/GeneralsMD/Code/Tools/WorldBuilder/src/WorldBuilder.cpp index ba2930f30a0..03b2f00f25b 100644 --- a/GeneralsMD/Code/Tools/WorldBuilder/src/WorldBuilder.cpp +++ b/GeneralsMD/Code/Tools/WorldBuilder/src/WorldBuilder.cpp @@ -35,6 +35,7 @@ #include "W3DDevice/GameClient/W3DFileSystem.h" #include "Common/FramePacer.h" #include "Common/GlobalData.h" +#include "Common/MapData.h" #include "WHeightMapEdit.h" //#include "Common/GameFileSystem.h" #include "Common/FileSystem.h" @@ -345,6 +346,7 @@ BOOL CWorldBuilderApp::InitInstance() INI ini; initSubsystem(TheWritableGlobalData, new GlobalData(), "Data\\INI\\Default\\GameData", "Data\\INI\\GameData"); + initSubsystem(TheWriteableMapData, new MapData()); TheFramePacer = new FramePacer(); diff --git a/GeneralsMD/Code/Tools/WorldBuilder/src/WorldBuilderDoc.cpp b/GeneralsMD/Code/Tools/WorldBuilder/src/WorldBuilderDoc.cpp index dae135ea3e4..fbb0e1a2df8 100644 --- a/GeneralsMD/Code/Tools/WorldBuilder/src/WorldBuilderDoc.cpp +++ b/GeneralsMD/Code/Tools/WorldBuilder/src/WorldBuilderDoc.cpp @@ -33,6 +33,7 @@ #include "Common/ThingTemplate.h" #include "Common/ThingFactory.h" #include "Common/WellKnownKeys.h" +#include "Common/MapData.h" #include "GameClient/Line2D.h" #include "GameClient/View.h" @@ -42,7 +43,6 @@ #include "GameLogic/SidesList.h" #include "GameLogic/ScriptEngine.h" - #include "Compression.h" #include "CUndoable.h" #include "LayersList.h" @@ -1238,6 +1238,7 @@ BOOL CWorldBuilderDoc::OnNewDocument() // clear out map-specific text TheGameText->reset(); + TheWriteableMapData->reset(); TNewHeightInfo hi; hi.initialHeight = AfxGetApp()->GetProfileInt("GameOptions", "Default Map Height", 16); @@ -1404,16 +1405,26 @@ BOOL CWorldBuilderDoc::OnOpenDocument(LPCTSTR lpszPathName) // clear out map-specific text TheGameText->reset(); + TheWriteableMapData->reset(); AsciiString s = lpszPathName; + AsciiString s_mapini; const char* lastSep = s.reverseFind('\\'); if (lastSep != NULL) { s.truncateTo(lastSep - s.str() + 1); } + s_mapini = s; s.concat("map.str"); DEBUG_LOG(("Looking for map-specific text in [%s]", s.str())); TheGameText->initMapStringFile(s); + s_mapini.concat("map.ini"); + if (TheFileSystem->doesFileExist(s_mapini.str())) + { + INI ini; + ini.load(s_mapini, INI_LOAD_MAPDATA_ONLY, nullptr, true); + } + WbApp()->setCurrentDirectory(AsciiString(buf)); ::GetModuleFileName(NULL, buf, sizeof(buf)); if (char *pEnd = strrchr(buf, '\\')) {