0 of 0

File information

Last updated

Original upload

Created by

bountygiver

Uploaded by

bountygiver

Virus scan

Safe to use

Tags for this mod

8 comments

  1. Lingam
    Lingam
    • member
    • 5 kudos
    I don't expect to get an answer after almost a year of no activity but, is this compatible with all DLC?

    Edit: Is this even the latest version? Just discovered the mod on steam workshop with updates as late as august.
  2. deleted21530149
    deleted21530149
    • account closed
    • 5 kudos
    Interesting... So, is it better? From the description it sounds better. This seems like a fundamental dynamic. There are some interesting comments here, but no one is really giving experience of gameplay opinions. Does it work? Have you seen a difference?
  3. bozzo23
    bozzo23
    • member
    • 3 kudos
    Hello
    thanks for taking time to make and share mods.

    I have read xcom2 hit system do the following:
    if you have 100% hit chance , and 40% crit , you will never crit , because the 100% hit take allready the whole 100%

    if i understand good , your mod should solve this problem too , since it would do ,40% chance crit on 100% hit , so 40 et 60.
  4. UhuruNUru
    UhuruNUru
    • premium
    • 32 kudos
    Well you knew the code was C/C+, I hadn't even thought about it, I don't know a thing about any specific code, just using the mathematical logic, all code is based on, I know the maths better than any specific code.
    I know nothing of any debate on Reddit, link would help, but unless it's been proved to be the case, it's just speculation, no different than mine, except in conclusions.
    The code can only (be intended to) work one way, i want to know which and actually, unless XCOM 2's method is really identical to EU/EW's method, it makes no difference to this mods intended function.
    However XCOM 2 may function, as long as it's different to EU/EW, this mods purpose is unchanged, it applies EU/EW rules to XCOM 2 instead, my query isn't about the mods purpose, that's clear to me, whatever XCom 2 actually does.
     
    One point is Tables are mentioned as being created in the source comments, I don't know anything about tables, but if the tables are randomly generated, then even using the same table locations (selected using the single Randroll), the contents of the location can still be random.
    var bool bAllowCrit;                            // Ability will build crit into the hit table as applicable
     
    Frankly I think that random roll is irrelevent to crits, if it simply selects the table location used, it's how the table is created that seems to apply. I am learning more as I write this and refer to the source,
     
    The function using the random number doesn't mention it applying to crits though, so I'm not sure what ia applied where. yet.

    function InternalRollForAbilityHit(XComGameState_Ability kAbility, AvailableTarget kTarget, const out AbilityResultContext ResultContext, out EAbilityHitResult Result, out ArmorMitigationResults ArmorMitigated, out int HitChance)
    {
        local int i, RandRoll, Current, ModifiedHitChance;
        local EAbilityHitResult DebugResult, ChangeResult;
        local ArmorMitigationResults Armor;
        local XComGameState_Unit TargetState, UnitState;
        local XComGameState_Player PlayerState;
        local XComGameStateHistory History;
        local StateObjectReference EffectRef;
        local XComGameState_Effect EffectState;
        local bool bRolledResultIsAMiss, bModHitRoll;
        local bool HitsAreCrits;
        local string LogMsg;

    i'll give you my interpretation, given my admitted lack of C/C+ knowledge specifics, I could be wrong. Which is why I'm questioning if Crits are part of the same roll and not sayimg they are definitly not.
    It defines an Abilty and target and four outputs from the function, crits are again only true or false for this function
     
    function InternalRollForAbilityHit
     
    XComGameState_Ability kAbility
    AvailableTarget kTarget
     
    const out AbilityResultContext ResultContext
    out EAbilityHitResult Result,
    out ArmorMitigationResults ArmorMitigated,
    out int HitChance
     
    local bool HitsAreCrits;
     
    The key appears to be how the, crits are built into the hit table from this true or false crit statement.
    var bool bAllowCrit;                            // Ability will build crit into the hit table as applicable
     
    If a table of each to hit chance used, is created of 100 random locations with crits applied to 1 for each crit% and the randroll selects which location is used. the same roll would still give a random result.
    Using tables or arrays of numbers is something computers can do well, it's often easier to achieve using these number blocks, than by direct calculation.
    Need to know how the tables are generated and I cant see anything in the code doing that.
    1. diksonh
      diksonh
      • member
      • 0 kudos
      Oops i forgot to include the link:
      https://www.reddit.com/r/Xcom/comments/465qkf/visualization_of_how_hit_chance_crit_chance_and/
    2. bountygiver
      bountygiver
      • premium
      • 5 kudos
      how the tables are generated?

      In finalizeHitRolls

      m_ShotBreakdown.ResultTable[eHit_Hit] = chance to hit - chance to crit - chance to dodge * chance to hit
      m_ShotBreakdown.ResultTable[eHit_Crit] = chance to crit
      m_ShotBreakdown.ResultTable[eHit_Dodge] = chance to hit * chance to dodge

      and the first 4 elements of the eHit enum (ordered) is

      eHit_Hit, eHit_Crit, eHit_Dodge, eHit_Miss

      I don't modify HItsAreCrits at all to change how crits are rolled, that boolean is for special abilities that force shots to be crits
  5. UhuruNUru
    UhuruNUru
    • premium
    • 32 kudos
    {Edit} Read on forum, posts tab is putting smilies in code boxes, WTF they are meant to show exactly what's typed (Sort of vital for coding), once again the Posts Tab proves totally unfit for mod support.
    That abomination shouldn't exist, clicking the posts button should open the Forum page it actually is, not some ruined distorted mess, that ignores the formatting. {End Edit}
     
    I've not looked at what your mod does, just what you claim is the games method, with the caveat that I'm new to Unreal Engine, this is more like, "Are you sure about that?", query.

    It doesn't ring true with my experience, but that's not proof, I've certainly seen none crit hits, but can't say they had a crit chance in the first place.
    So we go to the souce file.
    D:\Steam\SteamApps\Common\XCOM 2 SDK\Development\Src\XComGame\Classes\X2AbilityToHitCalc_StandardAim.uc
     
    Putting the full source here is only going to confuse matters, so I'll limit it too the relevant parts.
     

    function RollForAbilityHit(XComGameState_Ability kAbility, AvailableTarget kTarget, out AbilityResultContext ResultContext)
    {
        local EAbilityHitResult HitResult;
        local int MultiIndex, CalculatedHitChance;
        local ArmorMitigationResults ArmorMitigated;

        if (bMultiTargetOnly)
        {
            ResultContext.HitResult = eHit_Success;
        }
        else
        {
            InternalRollForAbilityHit(kAbility, kTarget, ResultContext, HitResult, ArmorMitigated, CalculatedHitChance); Armor reduction applied
            ResultContext.HitResult = HitResult;
            ResultContext.ArmorMitigation = ArmorMitigated;
            ResultContext.CalculatedHitChance = CalculatedHitChance;

    Now we have the numbers for these variables
    HitResult
    CalculatedHitChance
    ArmorMitigated
     
    Now we come to the relevant function

    function InternalRollForAbilityHit(XComGameState_Ability kAbility, AvailableTarget kTarget, const out AbilityResultContext ResultContext, out EAbilityHitResult Result, out ArmorMitigationResults ArmorMitigated, out int HitChance)
    {
        local int i, RandRoll, Current, ModifiedHitChance;
        local EAbilityHitResult DebugResult, ChangeResult;
        local ArmorMitigationResults Armor;
        local XComGameState_Unit TargetState, UnitState;
        local XComGameState_Player PlayerState;
        local XComGameStateHistory History;
        local StateObjectReference EffectRef;
        local XComGameState_Effect EffectState;
        local bool bRolledResultIsAMiss, bModHitRoll;
        local bool HitsAreCrits;
        local string LogMsg;

        History = `XCOMHISTORY;

        `log("===" $ GetFuncName() $ "===", true, 'XCom_HitRolls');
        `log("Attacker ID:" @ kAbility.OwnerStateObject.ObjectID, true, 'XCom_HitRolls');
        `log("Target ID:" @ kTarget.PrimaryTarget.ObjectID, true, 'XCom_HitRolls');
        `log("Ability:" @ kAbility.GetMyTemplate().LocFriendlyName @ "(" $ kAbility.GetMyTemplateName() $ ")", true, 'XCom_HitRolls');

        ArmorMitigated = Armor;     //  clear out fields just in case
        HitsAreCrits = bHitsAreCrits;
        if (`CHEATMGR != none)
        {
            if (`CHEATMGR.bForceCritHits)
                HitsAreCrits = true;

            if (`CHEATMGR.bNoLuck)
            {
                `log("NoLuck cheat forcing a miss.", true, 'XCom_HitRolls');
                Result = eHit_Miss;            
                return;
            }
            if (`CHEATMGR.bDeadEye)
            {
                `log("DeadEye cheat forcing a hit.", true, 'XCom_HitRolls');
                Result = eHit_Success;
                if (HitsAreCrits)
                    Result = eHit_Crit;
                return;
            }
        }

        HitChance = GetHitChance(kAbility, kTarget, true);
        RandRoll = `SYNC_RAND_TYPED(100, ESyncRandType_Generic);
        Result = eHit_Miss;

        `log("=" $ GetFuncName() $ "=", true, 'XCom_HitRolls');
        `log("Final hit chance:" @ HitChance, true, 'XCom_HitRolls');
        `log("Random roll:" @ RandRoll, true, 'XCom_HitRolls');
        //  GetHitChance fills out m_ShotBreakdown and its ResultTable
        for (i = 0; i < eHit_Miss; ++i)     //  If we don't match a result before miss, then it's a miss.
        {
            Current += m_ShotBreakdown.ResultTable[i];
            ebugResult = EAbilityHitResult(i);
            `log("Checking table" @ DebugResult @ "(" $ Current $ ")...", true, 'XCom_HitRolls');
            if (RandRoll < Current)
            {
                Result = EAbilityHitResult(i);
                `log("MATCH!", true, 'XCom_HitRolls');
                break;
            }
        }    
        if (HitsAreCrits && Result == eHit_Success)
            Result = eHit_Crit;

        UnitState = XComGameState_Unit(History.GetGameStateForObjectID(kAbility.OwnerStateObject.ObjectID));
        TargetState = XComGameState_Unit(History.GetGameStateForObjectID(kTarget.PrimaryTarget.ObjectID));
        
        if (UnitState != none && TargetState != none)
        {
            foreach UnitState.AffectedByEffects(EffectRef)
            {
                EffectState = XComGameState_Effect(History.GetGameStateForObjectID(EffectRef.ObjectID));
                if (EffectState != none)
                {
                    if (EffectState.GetX2Effect().ChangeHitResultForAttacker(UnitState, TargetState, kAbility, Result, ChangeResult))
                    {
                        `log("Effect" @ EffectState.GetX2Effect().FriendlyName @ "changing hit result for attacker:" @ ChangeResult,true,'XCom_HitRolls');
                        Result = ChangeResult;
                    }
                }
            }
        }

    This section casts doubt on your assertion all hits with a crit chance apply that crit.

    if (`CHEATMGR.bForceCritHits)
                HitsAreCrits = true;

    This forces the functionality asserted, with this applied all hits are guaranteed crits. Which raises the question, why would that exist, if already the default behavior?.
    Then this compounds my doubt,

    protected function AddModifier(const int ModValue, const string ModReason, optional EAbilityHitResult ModType=eHit_Success)
    {
        if (bGuaranteedHit && ModType != eHit_Crit)             //  for a guaranteed hit, the only possible modifier is to allow for crit
            return;

        super.AddModifier(ModValue, ModReason, ModType);
    }

    "for a guaranteed hit, the only possible modifier is to allow for crit", which says even for Guaranteed hits crits can fail.
     
    Finally the key data the hit chance

    HitChance = GetHitChance(kAbility, kTarget, true);
        RandRoll = `SYNC_RAND_TYPED(100, ESyncRandType_Generic);
        Result = eHit_Miss;

        `log("=" $ GetFuncName() $ "=", true, 'XCom_HitRolls');
        `log("Final hit chance:" @ HitChance, true, 'XCom_HitRolls');
        `log("Random roll:" @ RandRoll, true, 'XCom_HitRolls');
        //  GetHitChance fills out m_ShotBreakdown and its ResultTable
        for (i = 0; i < eHit_Miss; ++i)     //  If we don't match a result before miss, then it's a miss.
        {
            Current += m_ShotBreakdown.ResultTable[i];
            ebugResult = EAbilityHitResult(i);
            `log("Checking table" @ DebugResult @ "(" $ Current $ ")...", true, 'XCom_HitRolls');
            if (RandRoll < Current)
            {
                Result = EAbilityHitResult(i);
                `log("MATCH!", true, 'XCom_HitRolls');
                break;
            }
        }    
        if (HitsAreCrits && Result == eHit_Success)
            Result = eHit_Crit;

    Here's where I think you may have misunderstood

    if (HitsAreCrits && Result == eHit_Success)
            Result = eHit_Crit;

    HitsAreCrits means a successful crit roll (it's a boolean the result must be True or False, as the forced crit setting shown above proves.
     
    If Crit is true and hit is true then hit is also a crit, is what I see this code saying.
     
    I have enough evidence too cast serious doubt on your claim for Vanilla's methodology, only my lack of experience with the Unreal Engine, stops me saying this proves you wrong, but it's more than enough to ask the question.
    I will make it clear, I'm not querying your mods existence, just what you think vanilla does.
    1. diksonh
      diksonh
      • member
      • 0 kudos
      I am not as technical as you do (I never took a C/C++ lesson).
      It is suspected on reddit that both hit chance and crit chance use the same RandRoll.

      And what you said can be explained by a logical error - it may not be developer's intention to use the same RandRoll for hit and crit.

      RandRoll = `SYNC_RAND_TYPED(100, ESyncRandType_Generic)
      If ESyncRandType_Generic is a global variable for crit calculation as well then it is the same roll.