/* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */
#include "BasicMapDamage.h"
#include "ReadMap.h"
#include "MapInfo.h"
#include "Rendering/Env/GrassDrawer.h"
#include "Sim/Misc/GroundBlockingObjectMap.h"
#include "Sim/Misc/LosHandler.h"
#include "Sim/Misc/QuadField.h"
#include "Sim/Units/Unit.h"
#include "Sim/Units/UnitHandler.h"
#include "Sim/Path/IPathManager.h"
#include "Sim/Features/FeatureHandler.h"
#include "System/TimeProfiler.h"
void CBasicMapDamage::Init()
{
mapHardness = mapInfo->map.hardness;
for (int a = 0; a terrainTypes[ttIndex].hardness);
invHardness[ttIndex] = 1.0f / rawHardness[ttIndex];
}
void CBasicMapDamage::TerrainTypeSpeedModChanged(int ttIndex)
{
const unsigned char* typeMap = readMap->GetTypeMapSynced();
// update all map-squares that reference this terrain-type (slow)
for (int tz = 0; tz < mapDims.hmapy; tz++) {
for (int tx = 0; tx < mapDims.hmapx; tx++) {
if (typeMap[tz * mapDims.hmapx + tx] != ttIndex)
continue;
pathManager->TerrainChange((tx 1) * mapDims.hmapx + (x >> 1)];
// prevent formation of spikes from isolated "soft spots"
// (one or two random squares with extremely low hardness
// surrounded by high-strength terrain)
float sumRawHardness = 0.0f;
float avgInvHardness = 0.0f;
for (int j = -1; j 1) + j, 0, mapDims.hmapy - 1);
const int tmx = Clamp((x >> 1) + i, 0, mapDims.hmapx - 1);
const int tti = typeMap[tmz * mapDims.hmapx + tmx];
sumRawHardness += (rawHardness[tti] * weightTable[(j + 1) * 3 + (i + 1)]);
}
}
avgInvHardness = 1.0f / sumRawHardness;
// FIXME: compensate for flattened ground under dead buildings
const float prevDif = curHeightMap[y * mapDims.mapxp1 + x] - orgHeightMap[y * mapDims.mapxp1 + x];
float explDif = baseStrength;
explDif *= craterTable[tableIdx];
explDif *= avgInvHardness;
// explDif *= invHardness[ttypeIdx];
if ((prevDif * explDif) > 0.0f)
explDif /= ((math::fabs(prevDif) / EXPLOSION_LIFETIME) + 1);
if (explDif < -0.3f && strength > 200.0f)
grassDrawer->RemoveGrass(float3(x * SQUARE_SIZE, 0.0f, y * SQUARE_SIZE));
SetExplosionSquare(explDif);
}
}
QuadFieldQuery qfQuery;
quadField.GetUnitsExact(qfQuery, pos, radius);
// calculate how much to raise or lower buildings in the explosion radius
// (while still keeping the ground below them flat)
for (const CUnit* unit: *qfQuery.units) {
if (!unit->blockHeightChanges)
continue;
if (!unit->IsBlocking())
continue;
float totalDif = 0.0f;
for (int z = unit->mapPos.y; z < unit->mapPos.y + unit->zsize; z++) {
for (int x = unit->mapPos.x; x < unit->mapPos.x + unit->xsize; x++) {
// calculate the distance and normalize it
const float expDist = pos.distance2D(float3(x * SQUARE_SIZE, 0.0f, z * SQUARE_SIZE));
const float relDist = std::min(1.0f, expDist * invRadius);
const unsigned int tableIdx = relDist * CRATER_TABLE_SIZE;
const unsigned int ttypeIdx = typeMap[(z >> 1) * mapDims.hmapx + (x >> 1)];
const float prevDif = curHeightMap[z * mapDims.mapxp1 + x] - orgHeightMap[z * mapDims.mapxp1 + x];
float explDif = baseStrength;
explDif *= craterTable[tableIdx];
explDif *= invHardness[ttypeIdx];
if ((prevDif * explDif) > 0.0f)
explDif /= ((math::fabs(prevDif) / EXPLOSION_LIFETIME) + 1);
totalDif += explDif;
}
}
if (totalDif != 0.0f) {
e.buildings.emplace_back();
ExploBuilding& eb = e.buildings.back();
eb.id = unit->id;
eb.dif = totalDif / (unit->xsize * unit->zsize);
eb.tx1 = unit->mapPos.x;
eb.tx2 = unit->mapPos.x + unit->xsize;
eb.tz1 = unit->mapPos.y;
eb.tz2 = unit->mapPos.y + unit->zsize;
}
}
}
void CBasicMapDamage::RecalcArea(int x1, int x2, int y1, int y2)
{
readMap->UpdateHeightMapSynced(SRectangle(x1, y1, x2, y2));
featureHandler.TerrainChanged(x1, y1, x2, y2);
{
SCOPED_TIMER("Sim::BasicMapDamage::Los");
losHandler->UpdateHeightMapSynced(SRectangle(x1, y1, x2, y2));
}
{
SCOPED_TIMER("Sim::BasicMapDamage::Path");
pathManager->TerrainChange(x1, y1, x2, y2, TERRAINCHANGE_DAMAGE_RECALCULATION);
}
}
void CBasicMapDamage::Update()
{
SCOPED_TIMER("Sim::BasicMapDamage");
for (unsigned int i = explUpdateQueueIdx, n = explosionUpdateQueue.size(); i < n; i++) {
Explo& e = explosionUpdateQueue[i];
if ((e.ttl--) Move(UpVector * b.dif, true);
}
if (e.ttl != 0)
continue;
RecalcArea(e.x1 - 1, e.x2 + 1, e.y1 - 1, e.y2 + 1);
}
// pop explosions that are no longer being processed
while (explUpdateQueueIdx < explosionUpdateQueue.size()) {
if (explosionUpdateQueue[explUpdateQueueIdx].ttl > 0)
return;
explUpdateQueueIdx += 1;
}
// no more explosions left to process, clear queue for real
explosionUpdateQueue.clear();
explUpdateQueueIdx = 0;
}