// // Initialize system // void Init(Bool editModeIn) { ASSERT(!sysInit); editMode = editModeIn; U16 initialVal = U16(0); ASSERT(WorldCtrl::CellMapX()); ASSERT(WorldCtrl::CellMapZ()); // Initialise arrays for (U32 t = 0; t < Game::MAX_TEAMS; t++) { teamRemap[t] = Game::MAX_TEAMS; invTeamRemap[t] = Game::MAX_TEAMS; teamDirtyClust[t] = NULL; teamLastRescan[t] = U32_MAX; } if (editMode) { // If we're in the editor, setup for all teams teamCount = Game::MAX_TEAMS; for (U32 t = 0; t < Game::MAX_TEAMS; t++) { teamRemap[t] = U8(t); invTeamRemap[t] = U8(t); } } else { teamCount = Team::NumTeams(); // Fill in remap entries for teams with id's for (U32 t = 0, t2 = 0; t < Game::MAX_TEAMS; t++) { Team *teamPtr = Team::Id2Team(t); if (teamPtr) { ASSERT(teamPtr->GetId() < Game::MAX_TEAMS) // Map team id back to sequential index teamRemap[teamPtr->GetId()] = U8(t2); // Map sequential team index to team id invTeamRemap[t2] = U8(teamPtr->GetId()); ++t2; } } ASSERT(t2 == teamCount) } #ifdef DEVELOPMENT { // Log remap tables and test sync char s1[Game::MAX_TEAMS+1]; char s2[Game::MAX_TEAMS+1]; s1[Game::MAX_TEAMS] = s2[Game::MAX_TEAMS] = 0; for (U32 i = 0; i < Game::MAX_TEAMS; i++) { s1[i] = teamRemap[i] < Game::MAX_TEAMS ? char(teamRemap[i] + '0') : '.'; s2[i] = invTeamRemap[i] < Game::MAX_TEAMS ? char(invTeamRemap[i] + '0') : '.'; } //LOG_DIAG(("SightRemap [%s] [%s]", s1, s2)) SYNC("SightRemap [" << s1 << "] [" << s2 << ']') } #endif // Allocate seeing array for (U32 level = 0; level < Map::LV_MAX; ++level) { for (U32 team = 0; team < teamCount; ++team) { seeMap[level][team] = new U16*[WorldCtrl::CellMapZ()]; seeMap[level][team][0] = new U16[WorldCtrl::CellMapX() * WorldCtrl::CellMapZ()]; for (U32 i = 1; i < WorldCtrl::CellMapZ(); ++i) { seeMap[level][team][i] = &seeMap[level][team][i - 1][WorldCtrl::CellMapX()]; } // Set initial fog value for all tiles for (U32 z = 0; z < WorldCtrl::CellMapZ(); ++z) { for (U32 x = 0; x < WorldCtrl::CellMapX(); ++x) { seeMap[level][team][z][x] = initialVal; } } } } // Round x clusters up to the nearest 8 for the bit array mapClustXon8 = ((WorldCtrl::ClusterMapX() + 7) & (~7)) >> 3; // Allocate cluster update array displayDirtyCells = new BitArray2d(WorldCtrl::CellMapX(), WorldCtrl::CellMapZ()); for (t = 0; t < teamCount; t++) { // Allocate team cluster update array teamDirtyClust[t] = new U8[mapClustXon8 * WorldCtrl::ClusterMapZ()]; // Undirty all clusters memset(teamDirtyClust[t], 0, mapClustXon8 * WorldCtrl::ClusterMapZ()); } // Initialise radius division lookup table for (U32 r = 0; r < MAXR; ++r) { invRadTbl[r] = (r == 0) ? 10000.0F : (1.0F / F32(r * WC_CELLSIZE)); } // Initialise the terrain - set it all to shrouded SetAllFog(Terrain::fogFactorsS32[0]); // Create varsys commands VarSys::RegisterHandler("coregame.sight", CmdHandler); #ifdef DEVELOPMENT // Development commands VarSys::CreateCmd("coregame.sight.info"); VarSys::CreateCmd("coregame.sight.map"); VarSys::CreateInteger("coregame.sight.debugmode", FALSE, VarSys::DEFAULT, &debugMode); VarSys::CreateInteger("coregame.sight.debugscan", FALSE, VarSys::DEFAULT, &debugScan); #endif // The update rate of LOS display moved to terrain.shroud.rate // VarSys::CreateInteger("coregame.sight.updaterate", 2, VarSys::DEFAULT, &displayRate); // displayRate->SetIntegerRange(0, 100); #ifdef DEVELOPMENT sweepTime.Reset(); unSweepTime.Reset(); updateDisp.Reset(); dirtyCellTime.Reset(); #endif // Should all units be visible? showAllUnits = FALSE; // System is initialised sysInit = TRUE; }
// // CellCostHeuristic // // Calculates the actual cost of moving from cell A to cell B. Returns FALSE // if transition is impassable, otherwise returns TRUE and cost value. // Should be consistent with the estimation heuristic above. // static Bool CellCostHeuristic ( S32 s, U32 ax, U32 az, U32 bx, U32 bz, FootPrint::Instance *footInstance, FootPrint::Type::Cell *footCell, U16 &cost ) { // Is the destination blocked if (data.blockArray->Get2(bx, bz)) { return (FALSE); } // Is source a second layer cell if (footCell) { ASSERT(footInstance); // Can we travel from a->b in this direction if (!footCell->CheckDir(RotateSuccessor(footInstance->GetDir(), s))) { return (FALSE); } } // Now check the destination cell if (TerrainData::UseSecondLayer(bx, bz)) { footInstance = &TerrainData::GetFootPrintInstance(bx, bz); footCell = &footInstance->GetTypeCell(bx, bz); // Can we travel from b->a in this direction if (!footCell->CheckDir(RotateSuccessor(footInstance->GetDir(), SuccessorOpposite(s)))) { return (FALSE); } } // Get the destination data cell TerrainData::Cell &bDataCell = TerrainData::GetCell(bx, bz); // Get the balance info MoveTable::BalanceData &d = MoveTable::GetBalance ( bDataCell.surface, data.request.tractionType ); // Call above method if (CanMoveToCell(d, bDataCell)) { // Get base cost value for this cell U16 base = (U16)((ax == bx || az == bz) ? 10 : 14); // Set the cost based on the speed of this traction over this cell cost = U16(base + (2 * U16(base * (1.0F - d.speed)))); // If we have a unit, check to see how much enemy // threat there is to this unit in the current cluster // and factor that into the cost if (data.request.unit.Alive()) { // Get the cluster from the cell MapCluster *cluster = WorldCtrl::CellsToCluster(bx, bz); Team *team = data.request.unit->GetTeam(); U32 ac = data.request.unit->UnitType()->GetArmourClass(); // How much threat is there to this unit ? U32 threat = cluster->ai.EvaluateThreat(team, Relation::ENEMY, ac); cost = U16(Clamp<U32>(0, threat + cost, U16_MAX >> 2)); // How much relative pain is there to this unit ? U32 pain = cluster->ai.GetPain(team->GetId(), ac); U32 totalPain = AI::Map::GetPain(team->GetId(), ac); if (totalPain) { if (totalPain == pain) { cost = U16(Clamp<U32>(0, cost + (U32_MAX >> 18), U16_MAX >> 2)); } else { cost = U16(Clamp<U32>(0, cost + (Utils::Div(pain, 0, totalPain) >> 18), U16_MAX >> 2)); } }
// // New and improved sweep // void Sweep(UnitObj *u) { ASSERT(sysInit); ASSERT(u); //ASSERT(u->GetTeam()); ASSERT(u->OnMap()); START(sweepTime); r = u->GetSeeingRange(); unit = u; cellX = u->cellX; cellZ = u->cellZ; ASSERT(r < MAXR) // Get sight map and mask maskLo = u->sightMap->GetBitMask(Map::LV_LO); mapLo = u->sightMap->GetByteMap(Map::LV_LO); // Reset max radius seen u->sightMap->lastR = S16_MIN; // Set teams that this sweep will provide LOS for Team *myTeam = u->GetTeam(); teamBits = 0; if (myTeam) { for (U32 team = 0; team < Game::MAX_TEAMS; ++team) { Team *other = Team::Id2Team(team); if (other) { if ( // Other team is an ally Team::TestUnitRelation(u, other, Relation::ALLY) || // Other team is giving us line of sight myTeam->GivingSightTo(other->GetId()) ) { ASSERT(teamRemap[team] < teamCount) Game::TeamSet(teamBits, teamRemap[team]); } } } } // Dirty cells that line of sight has changed in DirtyCells(cellX - r, cellZ - r, cellX + r, cellZ + r, teamBits); // Set up viewing radius if (r > 0) { r2 = r * r; r2Inv = 1.0f / F32(r2); // Get eye position eyePos = EyePosition(u); // In the code below the row and column of cells with the // same y and x value of the unit are processed twice but // their viewing information is only updated once. Stuff // the slight inneficiency (2*r extra comparisons). This // way we require 1/4 the memory and the code is more // elegant. // scan ++ quadrant quadrantX = POS; quadrantZ = POS; FillGradMap(); FillMaxGradMapPosZ(); CompareGradPosPos(); // Scan -+ quadrant quadrantX = NEG; FillGradMapRotated(); FillMaxGradMapPosZ(); CompareGradNegPos(); // Scan -- quadrant quadrantZ = NEG; FillGradMap(); FillMaxGradMapNegZ(); CompareGradNegNeg(); // Scan +- quadrant quadrantX = POS; FillGradMapRotated(); FillMaxGradMapNegZ(); CompareGradPosNeg(); } // Can always see cell that unit is occupying CanSee(cellX, cellZ, 0, 0, mapLo, maskLo, teamBits, Map::LV_LO); // Update scan info in unit's sight map u->sightMap->lastTeam = teamBits; u->sightMap->lastR = S16(r); u->sightMap->lastX = cellX; u->sightMap->lastZ = cellZ; u->sightMap->lastAlt = eyePos; STOP(sweepTime); }