Example #1
0
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);
}