// --------------------------------------------------------------------------- int CvUnitMovement::GetCostsForMove(const CvUnit* pUnit, const CvPlot* pFromPlot, const CvPlot* pToPlot) { CvPlayerAI& kPlayer = GET_PLAYER(pUnit->getOwner()); CvPlayerTraits* pTraits = kPlayer.GetPlayerTraits(); bool bFasterAlongRiver = pTraits->IsRiverMovementBonus(); bool bFasterInHills = pTraits->IsFasterInHills(); bool bIgnoreTerrainCost = pUnit->ignoreTerrainCost(); bool bAmphibious = pUnit ? pUnit->isRiverCrossingNoPenalty() : false; bool bHover = pUnit ? pUnit->IsHoveringUnit() : false; TeamTypes eUnitTeam = pUnit->getTeam(); CvTeam& kUnitTeam = GET_TEAM(eUnitTeam); int iMoveDenominator = GC.getMOVE_DENOMINATOR(); int iRegularCost = iMoveDenominator; //check embarkation bool bFullCostEmbarkStateChange = false; bool bCheapEmbarkStateChange = false; bool bFreeEmbarkStateChange = false; if (pUnit->CanEverEmbark()) { if(!pToPlot->needsEmbarkation(pUnit) && pFromPlot->needsEmbarkation(pUnit)) { // Is the unit from a civ that can disembark for just 1 MP? if(GET_PLAYER(pUnit->getOwner()).GetPlayerTraits()->IsEmbarkedToLandFlatCost()) bCheapEmbarkStateChange = true; #if defined(MOD_BALANCE_CORE_EMBARK_CITY_NO_COST) //If city, and player has disembark to city at reduced cost... if(pToPlot->isFriendlyCityOrPassableImprovement(pUnit->getOwner())) { if (kUnitTeam.isCityNoEmbarkCost()) bFreeEmbarkStateChange = true; else if (kUnitTeam.isCityLessEmbarkCost()) bCheapEmbarkStateChange = true; } #endif bFullCostEmbarkStateChange = !(bFreeEmbarkStateChange || bCheapEmbarkStateChange); } if(pToPlot->needsEmbarkation(pUnit) && !pFromPlot->needsEmbarkation(pUnit)) { // Is the unit from a civ that can embark for just 1 MP? if(GET_PLAYER(pUnit->getOwner()).GetPlayerTraits()->IsEmbarkedToLandFlatCost()) bCheapEmbarkStateChange = true; #if defined(MOD_BALANCE_CORE_EMBARK_CITY_NO_COST) //If city, and player has embark from city at reduced cost... if(pFromPlot->isFriendlyCityOrPassableImprovement(pUnit->getOwner())) { if (kUnitTeam.isCityNoEmbarkCost()) bFreeEmbarkStateChange = true; else if (kUnitTeam.isCityLessEmbarkCost()) bCheapEmbarkStateChange = true; } #endif bFullCostEmbarkStateChange = !(bFreeEmbarkStateChange || bCheapEmbarkStateChange); } } bool bRiverCrossing = pFromPlot->isRiverCrossing(directionXY(pFromPlot, pToPlot)); FeatureTypes eToFeature = pToPlot->getFeatureType(); CvFeatureInfo* pToFeatureInfo = (eToFeature > NO_FEATURE) ? GC.getFeatureInfo(eToFeature) : 0; TerrainTypes eToTerrain = pToPlot->getTerrainType(); CvTerrainInfo* pToTerrainInfo = (eToTerrain > NO_TERRAIN) ? GC.getTerrainInfo(eToTerrain) : 0; //route preparation bool bRouteTo = pToPlot->isValidRoute(pUnit); bool bRouteFrom = pFromPlot->isValidRoute(pUnit); bool bFakeRouteTo = pTraits->IsWoodlandMovementBonus() && (eToFeature == FEATURE_FOREST || eToFeature == FEATURE_JUNGLE); bool bFakeRouteFrom = pTraits->IsWoodlandMovementBonus() && (pFromPlot->getFeatureType() == FEATURE_FOREST || pFromPlot->getFeatureType() == FEATURE_JUNGLE); //ideally there'd be a check of the river direction to make sure it's the same river bool bMovingAlongRiver = pToPlot->isRiver() && pFromPlot->isRiver() && !bRiverCrossing; bFakeRouteTo = bFakeRouteTo || (bFasterAlongRiver && bMovingAlongRiver); bFakeRouteFrom = bFakeRouteFrom || (bFasterAlongRiver && bMovingAlongRiver); //in some cases we ignore terrain / feature cost if (bFasterInHills && pToPlot->isHills()) bIgnoreTerrainCost = true; if (bHover) bIgnoreTerrainCost = true; #if defined(MOD_BALANCE_CORE) if (MOD_BALANCE_CORE && bAmphibious && bRiverCrossing) bIgnoreTerrainCost = true; if (MOD_BALANCE_CORE && pTraits->IsMountainPass() && pToPlot->isMountain()) bIgnoreTerrainCost = true; #endif //---- // preparation done, now here comes the interesting part //---- if(pToPlot->isRoughGround() && pUnit->IsRoughTerrainEndsTurn()) { // Is a unit's movement consumed for entering rough terrain? return INT_MAX; } else if (bFullCostEmbarkStateChange) { //embark/disembark ends turn return INT_MAX; } else if(!pToPlot->isRevealed(pUnit->getTeam()) && pUnit->isHuman()) { //moving into unknown tiles ends the turn for humans (to prevent information leakage from the displayed path) return INT_MAX; } else if (bFreeEmbarkStateChange) { iRegularCost = 0; } else if (bCheapEmbarkStateChange) { iRegularCost = iMoveDenominator; } else if(pUnit->flatMovementCost() || pUnit->getDomainType() == DOMAIN_AIR) { iRegularCost = iMoveDenominator; } else if(pToPlot->isCity()) { iRegularCost = iMoveDenominator; } else { //if the unit ignores terrain cost, it can still profit from feature bonuses if (bIgnoreTerrainCost) iRegularCost = 1; else { iRegularCost = ((eToFeature == NO_FEATURE) ? (pToTerrainInfo ? pToTerrainInfo->getMovementCost() : 0) : (pToFeatureInfo ? pToFeatureInfo->getMovementCost() : 0)); // Hill cost is hardcoded if(pToPlot->isHills() || pToPlot->isMountain()) { iRegularCost += GC.getHILLS_EXTRA_MOVEMENT(); } if(bRiverCrossing && !bAmphibious) { iRegularCost += GC.getRIVER_EXTRA_MOVEMENT(); } } if(iRegularCost > 0) { iRegularCost = std::max(1, (iRegularCost - pUnit->getExtraMoveDiscount())); } //now switch to high-precision costs iRegularCost *= iMoveDenominator; if(pToPlot->isHills() && pUnit->isHillsDoubleMove()) { iRegularCost /= 2; } else if((eToFeature == NO_FEATURE) ? pUnit->isTerrainDoubleMove(eToTerrain) : pUnit->isFeatureDoubleMove(eToFeature)) { iRegularCost /= 2; } #if defined(MOD_PROMOTIONS_HALF_MOVE) else if((pToPlot->getFeatureType() == NO_FEATURE) ? pUnit->isTerrainHalfMove(pToPlot->getTerrainType()) : pUnit->isFeatureHalfMove(pToPlot->getFeatureType())) { iRegularCost *= 2; } #endif } //check routes if( !bHover && (bRouteFrom || bFakeRouteFrom) && (bRouteTo || bFakeRouteTo) && (!bRiverCrossing || kUnitTeam.isBridgeBuilding() || bAmphibious) ) { RouteTypes eFromRoute = bFakeRouteFrom ? ROUTE_ROAD : pFromPlot->getRouteType(); CvRouteInfo* pFromRouteInfo = GC.getRouteInfo(eFromRoute); int iFromMovementCost = pFromRouteInfo ? pFromRouteInfo->getMovementCost() : 0; int iFromFlatMovementCost = pFromRouteInfo ? pFromRouteInfo->getFlatMovementCost() : 0; RouteTypes eToRoute = bFakeRouteTo ? ROUTE_ROAD : pToPlot->getRouteType(); CvRouteInfo* pToRouteInfo = GC.getRouteInfo(eToRoute); int iToMovementCost = pToRouteInfo ? pToRouteInfo->getMovementCost() : 0; int iToFlatMovementCost = pToRouteInfo ? pToRouteInfo->getFlatMovementCost() : 0; //routes only on land int iBaseMoves = pUnit->baseMoves(DOMAIN_LAND); int iRouteCost = std::max(iFromMovementCost + kUnitTeam.getRouteChange(eFromRoute), iToMovementCost + kUnitTeam.getRouteChange(eToRoute)); int iRouteFlatCost = std::max(iFromFlatMovementCost * iBaseMoves, iToFlatMovementCost * iBaseMoves); iRegularCost = std::min(iRegularCost, std::min(iRouteCost,iRouteFlatCost)); } //check border obstacles TeamTypes eTeam = pToPlot->getTeam(); if(eTeam != NO_TEAM) { CvTeam* pPlotTeam = &GET_TEAM(eTeam); CvPlayer* pPlotPlayer = &GET_PLAYER(pToPlot->getOwner()); // Great Wall increases movement cost by 1 if(pPlotTeam->isBorderObstacle() || pPlotPlayer->isBorderObstacle()) { if(!pToPlot->isWater() && pUnit->getDomainType() == DOMAIN_LAND) { // Don't apply penalty to OUR team or teams we've given open borders to if(eUnitTeam != eTeam && !pPlotTeam->IsAllowsOpenBordersToTeam(eUnitTeam)) { iRegularCost += iMoveDenominator; } } } #if defined(MOD_BALANCE_CORE) else if (eUnitTeam != eTeam) { //cheap checks first if(!pToPlot->isWater() && pUnit->getDomainType() == DOMAIN_LAND) { //Plots worked by city with movement debuff reduce movement speed. CvCity* pCity = pToPlot->getWorkingCity(); if(pCity != NULL) { if(pCity->GetBorderObstacleCity() > 0) { // Don't apply penalty to OUR team or teams we've given open borders to if(!pPlotTeam->IsAllowsOpenBordersToTeam(eUnitTeam)) { iRegularCost += iMoveDenominator; } } } } if(pToPlot->isWater() && (pUnit->getDomainType() == DOMAIN_SEA || pToPlot->needsEmbarkation(pUnit))) { //Plots worked by city with movement debuff reduce movement speed. CvCity* pCity = pToPlot->getWorkingCity(); if(pCity != NULL) { if(pCity->GetBorderObstacleWater() > 0) { // Don't apply penalty to OUR team or teams we've given open borders to if(!pPlotTeam->IsAllowsOpenBordersToTeam(eUnitTeam)) { iRegularCost += iMoveDenominator; } } } } } #endif } return iRegularCost; }
// --------------------------------------------------------------------------- void CvUnitMovement::GetCostsForMove(const CvUnit* pUnit, const CvPlot* pFromPlot, const CvPlot* pToPlot, int iBaseMoves, int& iRegularCost, int& iRouteCost, int& iRouteFlatCost) { CvPlayerAI& kPlayer = GET_PLAYER(pUnit->getOwner()); CvPlayerTraits* pTraits = kPlayer.GetPlayerTraits(); bool bFasterAlongRiver = pTraits->IsFasterAlongRiver(); bool bFasterInHills = pTraits->IsFasterInHills(); bool bIgnoreTerrainCost = pUnit->ignoreTerrainCost(); //int iBaseMoves = pUnit->baseMoves(isWater()?DOMAIN_SEA:NO_DOMAIN); TeamTypes eUnitTeam = pUnit->getTeam(); CvTeam& kUnitTeam = GET_TEAM(eUnitTeam); int iMoveDenominator = GC.getMOVE_DENOMINATOR(); bool bRiverCrossing = pFromPlot->isRiverCrossing(directionXY(pFromPlot, pToPlot)); FeatureTypes eFeature = pToPlot->getFeatureType(); CvFeatureInfo* pFeatureInfo = (eFeature > NO_FEATURE) ? GC.getFeatureInfo(eFeature) : 0; TerrainTypes eTerrain = pToPlot->getTerrainType(); CvTerrainInfo* pTerrainInfo = (eTerrain > NO_TERRAIN) ? GC.getTerrainInfo(eTerrain) : 0; if(bIgnoreTerrainCost || (bFasterAlongRiver && pToPlot->isRiver()) || (bFasterInHills && pToPlot->isHills())) { iRegularCost = 1; } else { iRegularCost = ((eFeature == NO_FEATURE) ? (pTerrainInfo ? pTerrainInfo->getMovementCost() : 0) : (pFeatureInfo ? pFeatureInfo->getMovementCost() : 0)); // Hill cost, except for when a City is present here, then it just counts as flat land if((PlotTypes)pToPlot->getPlotType() == PLOT_HILLS && !pToPlot->isCity()) { iRegularCost += GC.getHILLS_EXTRA_MOVEMENT(); } if(iRegularCost > 0) { iRegularCost = std::max(1, (iRegularCost - pUnit->getExtraMoveDiscount())); } } // Is a unit's movement consumed for entering rough terrain? if(pToPlot->isRoughGround() && pUnit->IsRoughTerrainEndsTurn()) { iRegularCost = INT_MAX; } else { if(!(bIgnoreTerrainCost || bFasterAlongRiver) && bRiverCrossing) { iRegularCost += GC.getRIVER_EXTRA_MOVEMENT(); } iRegularCost *= iMoveDenominator; if(pToPlot->isHills() && pUnit->isHillsDoubleMove()) { iRegularCost /= 2; } else if((eFeature == NO_FEATURE) ? pUnit->isTerrainDoubleMove(eTerrain) : pUnit->isFeatureDoubleMove(eFeature)) { iRegularCost /= 2; } } iRegularCost = std::min(iRegularCost, (iBaseMoves * iMoveDenominator)); if(pFromPlot->isValidRoute(pUnit) && pToPlot->isValidRoute(pUnit) && ((kUnitTeam.isBridgeBuilding() || !(pFromPlot->isRiverCrossing(directionXY(pFromPlot, pToPlot)))))) { CvRouteInfo* pFromRouteInfo = GC.getRouteInfo(pFromPlot->getRouteType()); CvAssert(pFromRouteInfo != NULL); int iFromMovementCost = (pFromRouteInfo != NULL)? pFromRouteInfo->getMovementCost() : 0; int iFromFlatMovementCost = (pFromRouteInfo != NULL)? pFromRouteInfo->getFlatMovementCost() : 0; CvRouteInfo* pRouteInfo = GC.getRouteInfo(pToPlot->getRouteType()); CvAssert(pRouteInfo != NULL); int iMovementCost = (pRouteInfo != NULL)? pRouteInfo->getMovementCost() : 0; int iFlatMovementCost = (pRouteInfo != NULL)? pRouteInfo->getFlatMovementCost() : 0; iRouteCost = std::max(iFromMovementCost + kUnitTeam.getRouteChange(pFromPlot->getRouteType()), iMovementCost + kUnitTeam.getRouteChange(pToPlot->getRouteType())); iRouteFlatCost = std::max(iFromFlatMovementCost * iBaseMoves, iFlatMovementCost * iBaseMoves); } else if(pUnit->getOwner() == pToPlot->getOwner() && (eFeature == FEATURE_FOREST || eFeature == FEATURE_JUNGLE) && pTraits->IsMoveFriendlyWoodsAsRoad()) { CvRouteInfo* pRoadInfo = GC.getRouteInfo(ROUTE_ROAD); iRouteCost = pRoadInfo->getMovementCost(); iRouteFlatCost = pRoadInfo->getFlatMovementCost() * iBaseMoves; } else { iRouteCost = INT_MAX; iRouteFlatCost = INT_MAX; } TeamTypes eTeam = pToPlot->getTeam(); if(eTeam != NO_TEAM) { CvTeam* pPlotTeam = &GET_TEAM(eTeam); CvPlayer* pPlotPlayer = &GET_PLAYER(pToPlot->getOwner()); // Great Wall increases movement cost by 1 if(pPlotTeam->isBorderObstacle() || pPlotPlayer->isBorderObstacle()) { if(!pToPlot->isWater() && pUnit->getDomainType() == DOMAIN_LAND) { // Don't apply penalty to OUR team or teams we've given open borders to if(eUnitTeam != eTeam && !pPlotTeam->IsAllowsOpenBordersToTeam(eUnitTeam)) { iRegularCost += iMoveDenominator; } } } } }