Jump to content

Still no way to improve cover?


PSPSoldier534

Recommended Posts

What exactly are you trying to change about cover? there are skills to improve defense and cover..with more information i may could help.

 

Some folks feel that if a soldier is in full cover and hunkered he should be 100% (or close to) safe unless he is flanked and vice versa for the aliens. Right now thats not the case. I play my games moving my soldiers exclusivly to high cover so that I avoid taking damage but I still get hit from offscreen or across insane distances. My only option is basically to savescum. The other is to use one soldier to pull aliens into killing zones which becomes really effective once you get high end snipers but still feels like cheating and is completly counter to the supposed tactical game where we are supposed to flank and overwatch.

 

The ideal game was Full Spectrum Warrior. If you were in cover and your enemy was in cover no matter how much lead was put down range neither side took damage (unless the cover was destroyable) and you had to flank or use special abilities to take out your target.

 

This should be the case with xcom.

If you are out in the open one hit=one kill (with exceptions for high end armour and aliens like mutons)

half cover no hunker= small chance of being killed or damaged (you expose yourself when firing but its so short that it would have to be a perfect shot)

half cover and hunker/full cover no hunker= small chance of taking minor damage (the cover hides your head/torso but your limbs can still be hit/ your arms/legs are exposed as you are firing but again require a perfect shot)

full cover hunker (you are completly hidden from the enemy and short of taking out the cover you cant be hit by anything)

 

That was at least my initial interest in modding cover but i have no really knowledge in this area and was simply bringing the idea up so people who could take a look might. Not sure if others who are interested feel the same way so hopefully if they disagree with me they'll post an answer for you.

Edited by DaGawdfadda
Link to comment
Share on other sites

I spend several hours looking for functions using cover values to modify hit chances in hope to find where exactly are these values defined.

 

Code was (purposely?) made mess, some crucial functions and classes properties etc have "dynamic" names, that are only uses locally.

Then there are replication functions, that I do not fully understand, they seem to propagate some properties (maybe functions too) "outside" of the class, but not always under same name.

So, even if you find where cover values are set, or read, there will be NO reference to such variable/function name in any other class. This could be "replicated" under another name, or sometimes there are arrays of variables and code is looking by index number, without referencing to specific name.

They are also function names or variables inherited while calling from another class, again makes back-tracing a road through hell.

 

Some examples what we have to deal with:

 

I started looking for for function, that generates hit or miss (true/false) while shooting.

Found it in class

XGAbility_Targeted

function RollForHit()

 

function RollForHit()
{
local float fRoll, fChance, fDist, fScatter;
local int iAdjustedChance, iRoll;
local XGAction_Fire kFireAction;
local XGParamTag kTag;
local Vector VDir, vDest;
local Rotator rRotate;
local XComUIBroadcastWorldMessage kBroadcastWorldMessage;

kTag = XGParamTag(XComEngine(class'Engine'.static.GetEngine()).LocalizeContext.FindTag("XGParam"));
m_bHit = false;
kFireAction = XGAction_Fire(m_kUnit.GetAction());
(...)

 

Code is long, there are some checks for "special" attacks, like rocket or psi, or debug cheat values (deadeye = always hit, noluck = always miss").

In the end, what this function does for standard shot is:

-calculate adjusted shot percentage and converting it to float 0..1:

iAdjustedChance = AdjustToHit(GetHitChance());
fChance = float(iAdjustedChance) / 100.00;

 

And now, setting hit to true/false:

m_bHit = XComGameReplicationInfo(class'Engine'.static.GetCurrentWorldInfo().GRI).m_kGameCore.RollForHit(fChance, m_kUnit.GetCharacter().m_kChar, GetPrimaryTarget().GetCharacter().m_kChar, fRoll);

 

...??? What is this ? These four parameters are probably (hit chanse, attacker, target, ??? ).

 

OK, did not go so well. so let us check function AdjustToHit. Maybe there are cover adjustments.

 

XGAbility_Targeted

function int AdjustToHit(int iHitChance)

 

function int AdjustToHit(int iHitChance)
{
local int iMissAdjustment, iAdjustment, iAdjustedHitChance;
local XGPlayer kPlayer;

(...)

 

Again, we have some checks for specials. No cover, perks & abilities only, so I do not paste this function here, if you want more, look at it in UE Explorer.

You can for example find, that difficulty leave is directly used to influence hit chances, and that there are friendly fire checks in the code.

 

 

OK, we go back.

 

We had:

"iAdjustedChance = AdjustToHit(GetHitChance()); "

 

GetHitChance() look interesting. It is used as base hit chance parameter, in another function call.

 

Searching...

XGAbility_Targeted

simulated function int GetHitChance()
{
return m_iHitChance;
}

 

Simulated. Don't really remember what it means, I guess that just returns m_iHitChance as value.

So, m_iHitChance must be set somewhere else.

The only point where it is calculated is:

 

XGAbilityTree

kAbility.m_iHitChance = kAbility.CalcHitChance();

 

Looking for CalcHitChance in classes...

XGAbility_Targeted

native function int CalcHitChance();

 

Function is there, but it is empty. No other definition in other classes, it is being called several times, but no trace of actually calculating its value.

 

That is it. I'm done. Lost many hours of life (what you see is only one of several paths through code I made).

They all ended up with some nonsense (empty) function where the was supposed to be a crucial calculation.

 

It was about fifteen years since I did things like this (yes, I'm so old), do not be to harsh if I wrote some nonsense. or made mistakes.

I expect someone makes better work then I did.

Link to comment
Share on other sites

UnrealScript Functions at http://udn.epicgames.com

 

Simulated

Declares that a function may execute on the client-side when an actor is either a simulated proxy or an autonomous proxy. All native functions are automatically simulated as well. (Note: if you override a virtual native with a non-native function' date=' the non-native override will NOT be simulated unless this keyword is specified).[/indent']

Native

You can declare UnrealScript functions as native, which means that the function is callable from UnrealScript, but is actually implemented (elsewhere) in C++. For example, the Actor class contains a lot of native function definitions, such as:

native(266) final function bool Move( vector Delta );

The number inside the parenthesis after the native keyword corresponds to the number of the function as it was declared in C++ (using the AUTOREGISTER_NATIVE macro), and is only required for operator functions. The native function is expected to reside in the DLL named identically to the package of the class containing the UnrealScript definition.

Well, there is no XComGame.dll, but maybe it is in thegame's executable.

 

Also UE Explorer 1.2 shows:

// Export UXGAbility_Targeted::execCalcHitChance(FFrame&, void* const)
native function int CalcHitChance();

 

Many searches end with native functions. Hope we will be able to see what are they doing one day.
Edited by Drakous79
Link to comment
Share on other sites

Thanks for tips.

It is my first contact with UE, I only wrote some simple software in C++ years ago.

 

Anyway, by looking in code you can easily see, that lot of constants were changed to numbers before retail exe was compiled.

Like here:

XGAbility_Targeted.h

 

coverPerks = kTarget.GetTacticalSenseCoverBonus();
       // End:0x211
       if(kTarget.HasBonus(15) && kTarget.HasHeightAdvantageOver(m_kUnit))
       {
           coverPerks += XComGameReplicationInfo(class'Engine'.static.GetCurrentWorldInfo().GRI).m_kGameCore.10; //high advantage defense bonus = 10
       }
       // End:0x29e
       if(kTarget.m_bInSmokeBomb)
       {
           coverPerks += XComGameReplicationInfo(class'Engine'.static.GetCurrentWorldInfo().GRI).m_kGameCore.20; //smoke bomb defense bonus = 20
       }
       // End:0x32b
       if(kTarget.m_bInDenseSmoke)
       {
           coverPerks += XComGameReplicationInfo(class'Engine'.static.GetCurrentWorldInfo().GRI).m_kGameCore.20; //dense smoke defense bonus = 20 
       }
       coverBonus = Max(0, kTarget.m_iCurrentCoverValue - coverPerks);
       // End:0x3ed
       if(kTarget.HasAirEvadeBonus())
       {
           iEvasionBonus = XComGameReplicationInfo(class'Engine'.static.GetCurrentWorldInfo().GRI).m_kGameCore.20; //air evade defense bonus = 20
       }
       coverBonus = Max(0, coverBonus - iEvasionBonus);

 

 

or here :

 

if(coverBonus != 0 && !m_bHasFlank)
                       {
                           // End:0x817
                           if(coverBonus == XComGameReplicationInfo(class'Engine'.static.GetCurrentWorldInfo().GRI).m_kGameCore.20)  // compares cover bonus DIRECTLY to 20 !
                         
                           {
                               kInfo.arrHitPenaltyStrings.AddItem(m_strPenaltyLowCover);
                           }
                           // End:0x840
                           else
                           {
                               kInfo.arrHitPenaltyStrings.AddItem(m_strPenaltyHighCover);
                           }
                           kInfo.arrHitPenaltyValues.AddItem(-coverBonus);
                           // End:0x948
                           if(kTarget.IsAffectedByAbility(38))
                           {
                               kInfo.arrHitPenaltyStrings.AddItem(m_strHunker);
                               kInfo.arrHitPenaltyValues.AddItem(XComGameReplicationInfo(class'Engine'.static.GetCurrentWorldInfo().GRI).m_kGameCore.2 - 1 * -coverBonus);
                            //   "1 *" means hunker down cover bonus = base cover bonus. (they stack).
                           }
                       }

 

Not really modding friendly way to compile.

 

BTW I thought about making hunker down check always true to make cover twice effective.

 

Or, sacrifice "Dense smoke" perk to make both cover values +20 by changing it's check to :

       if(kTarget.IsInCover())
       {
           coverPerks += XComGameReplicationInfo(class'Engine'.static.GetCurrentWorldInfo().GRI).m_kGameCore.20; //dense smoke defense bonus is now always applied when in cover
       }

 

Could anyone try to make such changes ?

I'm not sure if I can.

 

EDIT: I think it would not work anyway, this function is probably only to generate values to display on UI target info screen.

Edited by Just6669
Link to comment
Share on other sites

Dense smoke - you are genius :) Not sure, if we can replace a string with a function. Hope we can!

 

Edit: Ah I see, it is kTarget. My eyes need rest.

Edited by Drakous79
Link to comment
Share on other sites

I went through classes again. I think I understand cover logic pretty well, unfortunately crucial functions like:

 

// Export UXGUnitNativeBase::execSetCoverValue(FFrame&, void* const)
native simulated function SetCoverValue(int iNewCoverValue);
// Export UXGUnitNativeBase::execGetTacticalSenseCoverBonus(FFrame&, void* const)
native simulated function int GetTacticalSenseCoverBonus();
// Export UXGUnitNativeBase::execGetLowProfileCoverBonus(FFrame&, void* const)
native simulated function int GetLowProfileCoverBonus();

are virtual, so unit cover values are set from "outside" of what we can now change.

Dead end.

 

But.

I think we can adjust hit chance "manually", because

function bool RollForHit(float fChance, out TCharacter kShooter, out TCharacter kTarget, out float fRoll)
{
   fRoll = class'XComEngine'.static.SyncFRand(string(Name) @ string(GetStateName()) @ string(GetFuncName()));
   return fRoll <= fChance;
}

Is not native and all we have to do is adjust local fChance parameter to increase cover influence somewhere before this call:

 

           m_bHit = XComGameReplicationInfo(class'Engine'.static.GetCurrentWorldInfo().GRI).m_kGameCore.RollForHit(fChance, m_kUnit.GetCharacter().m_kChar, GetPrimaryTarget().GetCharacter().m_kChar, fRoll);

 

Function returning hit or miss itself is again in not accessible class 'Engine', but parameter fChance is local, and it is adjusted just a few lines above:

 

           if(XComTacticalCheatManager(GetALocalPlayerController().CheatManager).bDeadEye)
           {
               fChance = 1.0;
           }
           // End:0xc31
           else
           {
               // End:0xbf7
               if(XComTacticalCheatManager(GetALocalPlayerController().CheatManager).bNoLuck)
               {
                   fChance = 0.0;
               }
               // End:0xc31
               else
               {
                   iAdjustedChance = AdjustToHit(GetHitChance());
                   fChance = float(iAdjustedChance) / 100.0;
               }
           }
           m_bHit = XComGameReplicationInfo(class'Engine'.static.GetCurrentWorldInfo().GRI).m_kGameCore.RollForHit(fChance, m_kUnit.GetCharacter().m_kChar, GetPrimaryTarget().GetCharacter().m_kChar, fRoll);
           // End:0x131a

 

You can see two checks, that we could use to make our change. They are normally used to force hit or miss by internal game cheat manager. This also suggests, that locally changing exit value will not crash the game, yes there are several checks in code, but in a local (network == 0) game all they do is activating cheated flag / string (to debug log?).

 

So, is anyone able to change these DeadEye and NoLuck checks to something, that will use

GetPrimaryTarget().XComGame.GetTacticalSenseCoverBonus() (will be 20 or 40) to reduce fchance?

 

maybe sth like this:

if  (GetPrimaryTarget().XComGame.GetTacticalSenseCoverBonus == 20)
 {  
    fChance = 0.8 * fChance // decreases hit change by another 20% if low cover
 }
else
{  
 if  (GetPrimaryTarget().XComGame.GetTacticalSenseCoverBonus == 40)
   { 
     fChance = 0.7 * fChance // decreases hit change by another 30% if high cover
   }
}

 

This adjusting would stack with default bonuses.

 

Is it doable ?

Link to comment
Share on other sites

nativ functions arent empty, they just arent coded in the ue code

But you already got everthing you need, you know how the functions are called and what is expected to return from them

 

Fire up the binary in your favorite debugger or dissambler and look for the functions, you should find anything you need

If the coverchances are hardcoded it should be easy to mod it

Link to comment
Share on other sites

Code fragments, which I pasted here, with numbers, are not used to calculate hit chance nor cover bonus.

They are just used to compare read cover value with hardcoded number, to generate UI (and debug?) text strings.

 

Cover bonus values (probably array of 4 integers, for every direction) are is probably set in:

 

// Export UXGUnitNativeBase::execSetCoverValue(FFrame&, void* const)
native simulated function SetCoverValue(int iNewCoverValue);

 

To find which cover value should be used for specified shot, game uses

 

xxx.XComGame.GetTacticalSenseCoverBonus

 

where xxx is any funtion/variable, that can define what actor is the target, for example

 

GetPrimaryTarget().XComGame.GetTacticalSenseCoverBonus

 

There are possibilities of multiple targets, that is why hit modifiers are used with such prefix.

 

So, if I understand you correctly, function SetCoverValue should be in class XGUnitNativeBase, and I can search it by offset given by UE ? When I get home I will give it a try.

Edited by Just6669
Link to comment
Share on other sites

So, here is the buffer of XComGame.XGUnitNativeBase.SetCoverValue

 

Offset    0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F   0 1 2 3 4 5 6 7 8 9 A B C D E F 

00000000  D1 33 00 00 47 55 00 00 00 00 00 00 CA 33 00 00  . 3 . . G U . . . . . . . 3 . .
00000010  00 00 00 00 00 00 00 00 CB 33 00 00 00 00 00 00  . . . . . . . . . 3 . . . . . .
00000020  3C 08 00 00 D1 3C 01 00 0B 00 00 00 07 00 00 00  < . . . . < . . . . . . . . . .
00000030  29 CB 33 00 00 0B 53 00 00 00 00 05 02 00 8B 61  ) . 3 . . . S . . . . . . . . a
00000040  00 00 00 00 00 00                                                       . . . . . .                    

 

After some "random clicking" I realized, that UE seems to recognize some "things".:

 

D1 33 00 00 Obj XGUnitNativeBase.GetTacticalSenseCoverBonus.ReturnValue

CA 33 00 00 Obj XGUnitNativeBase.CanUseCover

CB 33 00 00 Obj. XGUnitNativeBase.SetCoverValue.iNewCoverValue

8B 61 00 00 Name. SetCoverValue

 

How can I use the unidentified data to continue my hunt ?

Link to comment
Share on other sites

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...