NextWalkableTile is not at all reliable with edges
6GNTEDE4K6MYWCBF7YXRRJ6MD66QOTVW74QWNGBYJMNN37ABZJWAC
local float HeightDisadvantageMalus; //OR YOU WILL LOOSE HOURS TRYING TO UNDERSTAND WHY
local 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 cover
FarCover = 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 GROUNDS
NextTile = 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 cover
LosTile = 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 COVER
Alpha = 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 already
LosTile = 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 covered
if (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 cover
FarCoverBonus = 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, // Y
TTile 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;}