CGameHelper::BuildSquareStatus CGameHelper::TestBuildSquare(const float3& pos, const float buildHeight, const UnitDef* unitdef, const MoveDef* moveDef, CFeature*& feature, int allyteam, bool synced) { if (!pos.IsInMap()) { return BUILDSQUARE_BLOCKED; } BuildSquareStatus ret = BUILDSQUARE_OPEN; const int yardxpos = int(pos.x + 4) / SQUARE_SIZE; const int yardypos = int(pos.z + 4) / SQUARE_SIZE; CSolidObject* s = groundBlockingObjectMap->GroundBlocked(yardxpos, yardypos); if (s != NULL) { CFeature* f = dynamic_cast<CFeature*>(s); if (f != NULL) { if ((allyteam < 0) || f->IsInLosForAllyTeam(allyteam)) { if (!f->def->reclaimable) { ret = BUILDSQUARE_BLOCKED; } else { ret = BUILDSQUARE_RECLAIMABLE; feature = f; } } } else if (!dynamic_cast<CUnit*>(s) || (allyteam < 0) || (static_cast<CUnit*>(s)->losStatus[allyteam] & LOS_INLOS)) { if (s->immobile) { ret = BUILDSQUARE_BLOCKED; } else { ret = BUILDSQUARE_OCCUPIED; } } if ((ret == BUILDSQUARE_BLOCKED) || (ret == BUILDSQUARE_OCCUPIED)) { if (CMoveMath::IsNonBlocking(s, moveDef, pos, buildHeight)) { ret = BUILDSQUARE_OPEN; } } if (ret == BUILDSQUARE_BLOCKED) { return ret; } } const float groundHeight = ground->GetHeightReal(pos.x, pos.z, synced); if (!unitdef->floatOnWater || groundHeight > 0.0f) { // if we are capable of floating, only test local // height difference IF terrain is above sea-level const float* orgHeightMap = readmap->GetOriginalHeightMapSynced(); const float* curHeightMap = readmap->GetCornerHeightMapSynced(); #ifdef USE_UNSYNCED_HEIGHTMAP if (!synced) { orgHeightMap = readmap->GetCornerHeightMapUnsynced(); curHeightMap = readmap->GetCornerHeightMapUnsynced(); } #endif const int sqx = pos.x / SQUARE_SIZE; const int sqz = pos.z / SQUARE_SIZE; const float orgHgt = orgHeightMap[sqz * gs->mapxp1 + sqx]; const float curHgt = curHeightMap[sqz * gs->mapxp1 + sqx]; const float difHgt = unitdef->maxHeightDif; if (pos.y > std::max(orgHgt + difHgt, curHgt + difHgt)) { return BUILDSQUARE_BLOCKED; } if (pos.y < std::min(orgHgt - difHgt, curHgt - difHgt)) { return BUILDSQUARE_BLOCKED; } } if (!unitdef->IsAllowedTerrainHeight(moveDef, groundHeight)) ret = BUILDSQUARE_BLOCKED; return ret; }
void CBasicMapDamage::Explosion(const float3& pos, float strength, float radius) { if (!pos.IsInMap()) { return; } if (strength < 10.0f || radius < 8.0f) { return; } Explo* e = new Explo; e->pos = pos; e->strength = strength; e->ttl = 10; e->x1 = Clamp<int>((pos.x - radius) / SQUARE_SIZE, 1, mapDims.mapxm1); e->x2 = Clamp<int>((pos.x + radius) / SQUARE_SIZE, 1, mapDims.mapxm1); e->y1 = Clamp<int>((pos.z - radius) / SQUARE_SIZE, 1, mapDims.mapym1); e->y2 = Clamp<int>((pos.z + radius) / SQUARE_SIZE, 1, mapDims.mapym1); e->squares.reserve((e->y2 - e->y1 + 1) * (e->x2 - e->x1 + 1)); const float* curHeightMap = readMap->GetCornerHeightMapSynced(); const float* orgHeightMap = readMap->GetOriginalHeightMapSynced(); const unsigned char* typeMap = readMap->GetTypeMapSynced(); const float baseStrength = -math::pow(strength, 0.6f) * 3 / mapHardness; const float invRadius = 1.0f / radius; for (int y = e->y1; y <= e->y2; ++y) { for (int x = e->x1; x <= e->x2; ++x) { const CSolidObject* so = groundBlockingObjectMap->GroundBlockedUnsafe(y * mapDims.mapx + x); // do not change squares with buildings on them here if (so && so->blockHeightChanges) { e->squares.push_back(0.0f); continue; } // calculate the distance and normalize it const float expDist = pos.distance2D(float3(x * SQUARE_SIZE, 0, y * SQUARE_SIZE)); const float relDist = std::min(1.0f, expDist * invRadius); const unsigned int tableIdx = relDist * CRATER_TABLE_SIZE; float dif = baseStrength; dif *= craterTable[tableIdx]; dif *= invHardness[typeMap[(y / 2) * mapDims.hmapx + x / 2]]; // FIXME: compensate for flattened ground under dead buildings const float prevDif = curHeightMap[y * mapDims.mapxp1 + x] - orgHeightMap[y * mapDims.mapxp1 + x]; if (prevDif * dif > 0.0f) { dif /= math::fabs(prevDif) * 0.1f + 1; } e->squares.push_back(dif); if (dif < -0.3f && strength > 200.0f) { grassDrawer->RemoveGrass(float3(x * SQUARE_SIZE, 0.0f, y * SQUARE_SIZE)); } } } // calculate how much to offset the buildings in the explosion radius with // (while still keeping the ground below them flat) const std::vector<CUnit*>& units = quadField->GetUnitsExact(pos, radius); for (const CUnit* unit: 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, z * SQUARE_SIZE)); const float relDist = std::min(1.0f, expDist * invRadius); const unsigned int tableIdx = relDist * CRATER_TABLE_SIZE; float dif = baseStrength * craterTable[tableIdx] * invHardness[typeMap[(z / 2) * mapDims.hmapx + x / 2]]; const float prevDif = curHeightMap[z * mapDims.mapxp1 + x] - orgHeightMap[z * mapDims.mapxp1 + x]; if (prevDif * dif > 0.0f) { dif /= math::fabs(prevDif) * 0.1f + 1; } totalDif += dif; } } totalDif /= (unit->xsize * unit->zsize); if (totalDif != 0.0f) { ExploBuilding eb; eb.id = unit->id; eb.dif = totalDif; eb.tx1 = unit->mapPos.x; eb.tx2 = unit->mapPos.x + unit->xsize; eb.tz1 = unit->mapPos.y; eb.tz2 = unit->mapPos.y + unit->zsize; e->buildings.push_back(eb); } } explosions.push_back(e); }