NextWalkableTile is not at all reliable with edges
6GNTEDE4K6MYWCBF7YXRRJ6MD66QOTVW74QWNGBYJMNN37ABZJWAC local float HeightDisadvantageMalus; //OR YOU WILL LOOSE HOURS TRYING TO UNDERSTAND WHYlocal float NoLineOfSightMalus; //THEY DIDN'T APPLY MULTIPLIERS!!! BECAUSE INT AUTO-ROUND!!! MOTHERFUCKERS!!!local string FarCoverText;local TTile NextTile;local int FarCover;local GameRulesCache_VisibilityInfo VisibilityInfo;
if ((NextTileOverCoverType == CT_MidLevel || NextTileOverCoverType == CT_Standing)){ExtendedLowCoverPreventingGoodAngleBonus = -Round(GoodAngleBonus / 2);`log("ExtendedLowCoverPreventingGoodAngleBonus:"@ExtendedLowCoverPreventingGoodAngleBonus, true, 'XCom_HitRolls');AddModifier(ExtendedLowCoverPreventingGoodAngleBonus , "ExtendedLowCover preventing GoodAngle", m_ShotBreakdown, eHit_Success, bDebugLog);} //WHEN ONLY "HALF OF" THE HIGHCOVER IS REDUCED BY THE GOODANGLE BECAUSE THE OTHER HALF IS COVERED BY LOWCOVER... ;)
ShooterHead = ShooterTile; ShooterHead.Z = `XWORLD.GetFloorTileZ(ShooterHead) + UnitState.UnitHeight -1;TargetHead = TargetTile; TargetHead.Z = `XWORLD.GetFloorTileZ(TargetHead ) + TargetState.UnitHeight -1;ShooterTorso = ShooterHead; ShooterTorso.Z -= 0.5;ShooterBelowFeet = ShooterTile; ShooterBelowFeet.Z -= 2;TargetLegs = TargetTile;
ShooterHead = ShooterTile; ShooterHead.Z = `XWORLD.GetFloorTileZ(ShooterHead) + UnitState.UnitHeight;TargetHead = TargetTile; TargetHead.Z = `XWORLD.GetFloorTileZ(TargetHead ) + TargetState.UnitHeight;
if (default.AllowFarCoverToGiveSomeCoverProtection || default.GiveHitChanceZeroWhenTargetableEnemiesAreInsteadNotTrulyInLineOfSight){// Both these modifiers are based off far coverFarCover = 0;PeekTile = VisInfo.SourceTile;PeekHead = PeekTile; PeekHead.Z = `XWORLD.GetFloorTileZ(PeekTile) + UnitState.UnitHeight;`log("PeekTile:"@PeekTile.X@","@PeekTile.Y@","@PeekTile.Z, true, 'XCom_HitRolls');`log("PeekHead:"@PeekHead.X@","@PeekHead.Y@","@PeekHead.Z, true, 'XCom_HitRolls');TargetPeekTile = VisInfo.DestTile;`log("TargetTile:"@TargetTile.X@","@TargetTile.Y@","@TargetTile.Z, true, 'XCom_HitRolls');`log("TargetPeekTile:"@TargetPeekTile.X@","@TargetPeekTile.Y@","@TargetPeekTile.Z, true, 'XCom_HitRolls');`log("Target Height:"@TargetState.UnitHeight, true, 'XCom_HitRolls');
if (default.GiveHitChanceZeroWhenTargetableEnemiesAreInsteadNotTrulyInLineOfSight){ //'FAR' COVER BLOCKING VISIBILITY CHECK...if (ShooterHead.Z - TargetLegs.Z >= 0) //SAME HEIGHT OR BELOW SHOOTER{NextTile = ShooterTile;NextTile = NextWalkableTile(ShooterTile, NextTile, TargetTile, FarCover); if (NextTile.Z>=ShooterTile.Z && FarCover>0 && !`XWORLD.CanSeeTileToTile(ShooterTorso, TargetHead, VisibilityInfo)) NoLineOfSightMalus = -1000;NextTile = NextWalkableTile(ShooterTile, NextTile, TargetTile, FarCover); if (NextTile.Z>=ShooterTile.Z && FarCover>0 && !`XWORLD.CanSeeTileToTile(ShooterTorso, TargetHead, VisibilityInfo)) NoLineOfSightMalus = -1000; //LOW COVER IN CALCULATING VISIBILITY.NextTile = NextWalkableTile(ShooterTile, NextTile, TargetTile, FarCover); if (NextTile.Z>=ShooterTile.Z && FarCover>0 && !`XWORLD.CanSeeTileToTile(ShooterTorso, TargetHead, VisibilityInfo)) NoLineOfSightMalus = -1000; //THIS NONSENSE HAD TO BE MODDED AWAY...}if (ShooterHead.Z - TargetLegs.Z < 0) //ABOVE SHOOTER{NextTile = ShooterTile;NextTile = NextWalkableTile(ShooterTile, NextTile, TargetTile, FarCover); if (FarCover == 1 && !`XWORLD.CanSeeTileToTile(ShooterBelowFeet, TargetLegs, VisibilityInfo)) NoLineOfSightMalus = -1000; //DIFFERENT CHECK FOR HIGH GROUNDSNextTile = NextWalkableTile(ShooterTile, NextTile, TargetTile, FarCover); if (FarCover == 1 && !`XWORLD.CanSeeTileToTile(ShooterBelowFeet, TargetLegs, VisibilityInfo)) NoLineOfSightMalus = -1000; //IN CALCULATING VISIBILITY.NextTile = NextWalkableTile(ShooterTile, NextTile, TargetTile, FarCover); if (FarCover == 1 && !`XWORLD.CanSeeTileToTile(ShooterBelowFeet, TargetLegs, VisibilityInfo)) NoLineOfSightMalus = -1000;}`log("NoLineOfSightMalus:"@NoLineOfSightMalus, true, 'XCom_HitRolls');If (NoLineOfSightMalus != 0) AddModifier(NoLineOfSightMalus, "No Line of Sight", m_ShotBreakdown, eHit_Success, bDebugLog);}
if (default.AllowFarCoverToGiveSomeCoverProtection && (TargetState.GetTeam() == eTeam_XCom || TargetState.GetCurrentStat(eStat_AlertLevel)==`ALERT_LEVEL_RED || (TargetState.GetCurrentStat(eStat_AlertLevel)!=`ALERT_LEVEL_RED && !default.OnlyAlertedTargetsCanHaveTheExtraProtectionOfFarCoverAndHeightDisadvantage)) && (VisInfo.TargetCover==CT_None || (GoodAngleBonus > 0 && ExtendedLowCoverPreventingGoodAngleBonus == 0)) && //FAR COVER ONLY IF NOT IN COVER OR IF THE COVER TAKEN IS NOT IN THE DIRECTION OF THE SHOOTER (MORE FLANKING THAN COVER)TileDistance > 2 && TargetState.GetMyTemplate().bCanTakeCover == true){ShooterTargetDistance = TilesDistance(TargetTile, ShooterTile); //SUCH A DANGERIOUSLY MISLEADING FUNCTION!!!!??? HOWRS TRYING TO UNDERSTAND WHY DISTANCE WAS WRONGLY CALCULATED!!!FarCoverText = "Far Cover";if (ShooterHeadRelativeElevation == 0)
// FarCover to a tile which may already be granting coverLosTile = TargetTile;LosTile.Z = `XWORLD.GetFloorTileZ(TargetTile);for (i=0; i<=TargetState.UnitHeight; i++)
}if (ShooterHead.Z - TargetLegs.Z < 0) //ABOVE SHOOTER{NextTile = TargetTile;for (i = 0; i < 5 && FarCoverBonus==0 && NextTile.Z == TargetTile.Z; ++i)
if (`XWORLD.VoxelRaytrace_Tiles(PeekHead, LosTile, RayTrace))
if (FarCoverBonus > 0 && GoodAngleBonus > 0) {FarCoverBonus = Min(GoodAngleBonus, FarCoverBonus); FarCoverText = "FarCover Preventing GoodAngle";}`log("FarCoverBonus:"@round(-FarCoverBonus), true, 'XCom_HitRolls');AddModifier(round(-FarCoverBonus), FarCoverText, m_ShotBreakdown, eHit_Success, bDebugLog);}
LosTile.Z++;}
if (default.ApplyVerticalBadAngleAgainstHigherTargetAsProportionalHeightDisadvantage && ShooterHead.Z - TargetLegs.Z < 0 //TARGET STANDING ON A SURFACE HIGHER THAN THE SHOOTER'S HEAD&& (TargetState.GetTeam() == eTeam_XCom || TargetState.GetCurrentStat(eStat_AlertLevel)==`ALERT_LEVEL_RED || (TargetState.GetCurrentStat(eStat_AlertLevel)!=`ALERT_LEVEL_RED && !default.OnlyAlertedTargetsCanHaveTheExtraProtectionOfFarCoverAndHeightDisadvantage)) && TargetState.GetMyTemplate().bCanTakeCover == true) //HIGHER, LOWER OR SAME LEVEL, FAR COVER MUST GIVE SOME ADVANTAGE NO MATTER THE HEIGHT{ //HEIGHT DISADVANTAGE DOESN'T INCREASE TARGET'S HIGH/LOW COVER... BUT ONLY ADDS FROM 1 TO 19 COVER WHEN BUILDING/CLIFF PARTIALLY ACTS AS LOW COVERAlpha = FClamp((90-(-VerticalAngle) - 0) / (85 - 0), 0.0, 1.0);AngleToCoverModifier = Lerp(1, 0, Alpha);HeightDisadvantageMalus = round(-AngleToCoverModifier*(20)); NextTile = TargetTile;if (VisInfo.TargetCover == CT_MidLevel) FarCover = 1;if (VisInfo.TargetCover != CT_MidLevel)
// FarCover for which cover hasn't been determined alreadyLosTile = TargetPeekTile;LosTile.Z = `XWORLD.GetFloorTileZ(TargetTile);for (i=0; i<=TargetState.UnitHeight; i++)
NextTile = NextWalkableTile(TargetTile, NextTile, ShooterTile, FarCover); if (NextTile.Z < TargetTile.Z) break;HeightDisadvantageMalus = HeightDisadvantageMalus + HeightDisadvantageMalus/2 ; //GIVES LARGE SUMS! ROUND AT THE END!!!
`log("LOS blocked by far cover:"@i, true, 'XCom_HitRolls');FarCover += 1;
}if (FarCover==1) HeightDisadvantageMalus = 2 * HeightDisadvantageMalus;AddModifier(round(HeightDisadvantageMalus),class'XLocalizedData'.default.HeightDisadvantage, m_ShotBreakdown,eHit_Success, bDebugLog); //POSITIVE OR NEGATIVE???
LosTile.Z++;}// Big things are easy to hit even if partially coveredif (TargetState.UnitHeight > 2){FarCover = Max(0, FarCover - (TargetState.UnitHeight - 2) * 2);}if (default.GiveHitChanceZeroWhenTargetableEnemiesAreInsteadNotTrulyInLineOfSight && FarCover == 12){`log("NoLineOfSightMalus: -1000", true, 'XCom_HitRolls');AddModifier(-1000, "No Line of Sight", m_ShotBreakdown, eHit_Success, bDebugLog);}else if (default.AllowFarCoverToGiveSomeCoverProtection && (TargetState.GetTeam() == eTeam_XCom || TargetState.GetCurrentStat(eStat_AlertLevel)==`ALERT_LEVEL_RED || (TargetState.GetCurrentStat(eStat_AlertLevel)!=`ALERT_LEVEL_RED && !default.OnlyAlertedTargetsCanHaveTheExtraProtectionOfFarCoverAndHeightDisadvantage))){// Scale far cover to high coverFarCoverBonus = FarCover * HIGH_COVER_BONUS / 12; // 12 is the maximum considered ray`log("FarCoverBonus:"@round(-FarCoverBonus), true, 'XCom_HitRolls');AddModifier(round(-FarCoverBonus), "Far Cover", m_ShotBreakdown, eHit_Success, bDebugLog);}
function bool CoverOnBothSides(TTile ShooterTile, TTile TargetTile){local TTile TileDifference;local int DirectionX;local int DirectionY;local TileData TargetTileData;TileDifference.X = TargetTile.X - ShooterTile.X;TileDifference.Y = TargetTile.Y - ShooterTile.Y;DirectionX = 0;DirectionY = 0;if( Abs(TileDifference.X) > Abs(TileDifference.Y) ) {if( TileDifference.X > 0) {DirectionX = `XWORLD.COVER_West;}if( TileDifference.X <=0) {DirectionX = `XWORLD.COVER_East;}}if( Abs(TileDifference.X) <= Abs(TileDifference.Y) ) {if( TileDifference.Y > 0) {DirectionY = `XWORLD.COVER_North;}if( TileDifference.Y <=0) {DirectionY = `XWORLD.COVER_South;}}`XWORLD.GetTileData(TargetTile, TargetTileData);if ((TargetTileData.CoverFlags & DirectionX) != 0 &&(TargetTileData.CoverFlags & DirectionY) != 0) return true;}static function float TilesDistance(TTile TileA, TTile TileB){local XComWorldData World;local vector LocA, LocB;World = `XWORLD;LocA = World.GetPositionFromTileCoordinates(TileA);LocB = World.GetPositionFromTileCoordinates(TileB);return VSize(LocA - LocB);}function TTile NextWalkableTile(TTile OriginTile, // YTTile MidTile, // ^TTile DestinationTile, // |out int FarCover) // X <--{local TTile TileDifference;local TTile NextTile;local int Direction;local int OriginDestinationAngle;local int MidTileDestinationAngle;local TileData NextTileData;OriginDestinationAngle = Abs(DestinationTile.X - OriginTile.X) / Abs(DestinationTile.Y - OriginTile.Y);MidTileDestinationAngle = Abs(DestinationTile.X - MidTile.X) / Abs(DestinationTile.Y - MidTile.Y);NextTile = MidTile;TileDifference.X = DestinationTile.X - MidTile.X;TileDifference.Y = DestinationTile.Y - MidTile.Y;Direction = 0;FarCover = 0;if (OriginDestinationAngle <= MidTileDestinationAngle){if( TileDifference.X > 0) {++NextTile.X;}if( TileDifference.X <=0) {--NextTile.X;}}if (OriginDestinationAngle > MidTileDestinationAngle){if( TileDifference.Y > 0) {++NextTile.Y;}if( TileDifference.Y <=0) {--NextTile.Y;}}if (Abs(TileDifference.X) > Abs(TileDifference.Y)){if( TileDifference.X > 0) {Direction = `XWORLD.COVER_WLow;}if( TileDifference.X <=0) {Direction = `XWORLD.COVER_ELow;}}if (Abs(TileDifference.X) <= Abs(TileDifference.Y)){if( TileDifference.Y > 0) {Direction = `XWORLD.COVER_NLow;}if( TileDifference.Y <=0) {Direction = `XWORLD.COVER_SLow;}}NextTile.Z = `XWORLD.GetFloorTileZ(NextTile);`XWORLD.GetTileData(NextTile, NextTileData);if ((NextTileData.CoverFlags & Direction) != 0) FarCover = 1;return NextTile;}