Jump to content

R&D Inventory, Items & Weapons Overhaul


anUser

Recommended Posts

The STORAGE() calls are all only available in the strategy game, and so aren't available from the tactical game at all. They also don't modify individual soldier inventory.

I said I made that up... I meant I called that from a function in XGStorage.

 

Another option is:

TInventorySmallItemsRemoveItem(...)

Tested, it doesn't seem to do anything. I've tested it in a custom function ReleaseSmallItems that actualy loops through small items, and I added the call to TInventorySmallItemsRemoveItem(kInventory, 85); so it should remove a grenade for each small item equipped, regardless they're grenades or not, but I couldn't notice any effect.

 

Moreover this confirms that besides removing item storage we need to remove the item from the soldiers' inventory. Just in case I tried to just release item claim and remove from storage, but then the items were still present in the inventory, so I could unequip them and thus compensate the items I'd just removed from storage. This also caused the number of items shown in engineering to be incorrect (that number was indeed decreased each battle, I guess it takes the value from storage), while in the lockers there were always the same amount of grenades.

 

I'll try next that native function HasItemOfType, in case it returned the slot index (first array index of found element or something). In any case I was thinkking that maybe there's some function to handle arrays that we could use, such some Find() or IsElementInArray() generic functions that could return the inventory slot index in the small items array in TInventory so we can call TInventorySmallItemsSetItem(kInventory, slot id, 0). Any ideas?

Link to comment
Share on other sites

  • Replies 129
  • Created
  • Last Reply

Top Posters In This Topic

Welll ... don't think of it as a bug, think of it as a feature. The number displayed in Engineering is the number of items "available in storage". This is a useful number to know!

 

As to the TInventorySmallItemsRemoveItem ... you are keeping track of the difference between soldier inventory and storage, right? The TInventorySmallItemsRemoveItem should only remove the item from the soldiers inventory, which would have no effect on the number of items in storage.

 

I'm pretty sure HasItemOfType returns a boolean and not an index.

 

There is a build in find byte-code, EX_DynArrayFind = 0x46 ... looks like it is dynamic array only, which is a shame, since the TInventory structure uses static arrays. Might be stuck with using a loop to check every item.

 

There is the function:

 

native simulated function XGInventoryItem FindItemByEnum(int iItem);
Unfortunately, this doesn't help us get access to the TInventory slot of the item (The XGInventoryItem class only has references to var XGInventorySlot m_kSlot; and var XGInventorySlot m_kReservedSlot;, which are not passed back to the strategy game).
There is a function TInventoryCustomItemsFind for custom items, but no counterpart for Small Items, unfortunately.
Link to comment
Share on other sites

Ok finally I think I've found a way to make spendable items... I'll lay it out so if anyone spots anything that might conflict with other enhancements or that may not work as intended please let me know. I'll have to investigate a bit how to code some stuff but I think it's doable and it won't be that hard to code.

 

The idea is to remove each item from inventory right when the appropiate ability is used. In XGAbilityTree, code the different abilities in a switch and store the item to remove in local variable iCost ...

 

switch (kAbility.GetType()) {
  case 22:
    kAbility.kUnit.iFragGrenades -= 1;
    iCost = 85;
etc...
};

and then use some XGUnit variable to loop through the inventory using that trick of multiplying a value and then calculating the module to store two values into one, so at the end of the function, before iCost is really calculated (that'd had to be moved), there could be a check of the sort:

 

kAbility.kUnit.iAlienGrenades += kAbility.kUnit.iFragGrenades*16;
kAbility.kUnit.iFragGrenades = iCost;  // stores item value
iCost = 0;
// implied jump
if (iCost < kAbility.kUnit.m_kCharacter.m_kChar.m_kInventory.arrSmallItems) {
  if (byte(kAbility.kUnit.m_kCharacter.m_kChar.m_kInventory.arrSmallItems[iCost]) == byte(kAbility.kUnit.iFragGrenades)) {
    kAbility.m_kUnit.m_kCharacter.m_kChar.m_kInventory.TInventorySmallItemsSetItem(kAbility.m_kUnit.m_kCharacter.m_kChar.m_kInventory, iCost, 0)
    RemoveItem(kAbility.kUnit.iFragGrenades, 1);
    RelseaseItem(kAbility.kUnit.iFragGrenades, kAbility.m_kUnit.m_kCharacter);
 }
  iCost++;
  jump back;
}
kAbility.kUnit.iFragGrenades = kAbility.kUnit.iAlienGrenades / 16;
kAbility.kUnit.iAlienGrenades =% 16;

I know it's a bit of a mess, any improvement is much appreciated, any way to optimize this code or rather different approaches to solve the matter of spendable items are more than welcome! I'll wait a bit before implementing this and see if some better solution comes out.

Link to comment
Share on other sites

This is somewhat related to this topic, I think.

 

I think I've figured out how the "Deep Pockets" perk works.

 

Inside XGStrategySoldier.GivePerk is the code:

case 53:
TACTICAL().TInventorySmallItemsAdd(m_kChar.kInventory, 1);
TACTICAL().TInventorySmallItemsSetItem(m_kChar.kInventory, 1, 85);
// End:0xEB
break;

 

ff

ePerk_DeepPockets = 53, and eItem_FragGrenade = 85.

 

It looks as though the TACTICAL().TInventorySmallItemsAdd(m_kChar.kInventory,1) actually is adding a small inventory SLOT to the character. That means that this could be replicated to grant extra small item inventory slots as a part of level progression, as opposed to granting it via perk.

 

One way that this could be used is by adding 1 inventory slots at ... say ... Major ... but making all of the end-game armors have no armor slots. This would provide penalties to sticking Rookies in late-game armor (they couldn't carry anything), but experienced soldiers could handle it.

 

Another possibility is creating a "Backpack" item. The item has move and defense penalties, but grants the equipper with 2 extra small item slots. Could even make it fit into the pistol slot to make it more worthwhile (otherwise it takes up one of the small item slots it creates ~_~).

 

Just some random thoughts. Not sure if anyone was looking or is interested in alternate ways to grant extra small item slots to soldiers (in addition to granting it via armor).

Link to comment
Share on other sites

Good point. I like both soldiers getting inventory slots as they rank up, and that backpack item for rookies or maybe only for support soldiers, to carry extra smoke grenades and medikits.

 

The truth is I haven't found yet an entirely satisfactory way to compensate multiple item choices with perks and finite storage, so this is also an important factor to consider. I'll sum up my impressions so far with several different settings:

- 3 inventory slots by default is too much, even if each item only grants 1 charge (ie, no grenadier, no field medic, no rocketeer). 2 still creates interesting combinations. If "exploited" and picking imo the 2 most powerful items, scope & armor vest, because they grant continuous effect, that still leaves the soldier with no "consumibles" such grenades, medikits, etc.

- having to build basic consumible items such as frag grenades and so isn't quite contributing to the strategic aspect of the game, right the opposite, all this popup screens in the geoscape telling you've got 8 or 12 new grenades aren't helping much. Also right at the beginning of the game is when you're more vulnerable so it's when you're in greatest need of extra grenades and armors and so, and it's also when you're funding is lower, so kind of forcing the player to spend money in this isn't quite appealing. On the other hand, I think that on the long run, once the player's income is respectable, having to spend some money on equipment is ok. So my thought now is finding a good amount of initial storage of each item, to make it so if the player makes reasonable use of the storage then that could last enough to allow the player to stabilize his/her economy, but if the player makes intensive use of consumibles then s/he may be in need of buying some more before "stabilizing".

- carrying extra charges is a nice option. I mean it's a fair tradeoff when you exchange a scope for an extra rocket, extra arc thrower battery, etc. It makes so you can priorize

- Free items given by perks is cool but not with many item slots (I mean making the grenadier perk gives 1 free grenade, smoke & mirrors 1 free smoke grenade, etc). Maybe with dynamic number of item slots this can be an interesting option.

 

I'll give it some more thought to this idea of dynamically expanding soldier's inventory. Right now there are a few possible combinations, either including the Deep Pockets perk several times in the perk tree (assuming we can select the same perk multiple times with Amineri's mod), so each item comes at expenses of an ability; or directly grant the extra item slots as the units rank up, from 0 up to 2 or 3. And this with or without free items from perks.

Link to comment
Share on other sites

My current version of the perk tree expansion disallows a player from selecting the same perk multiple times. Many perks have no additional benefit from being selecting more than once.

 

The inventory-related perks could have benefit, but the perk-managing code would need some rewriting to support selecting these perks multiple times and it having an effect. In fact, even after the perk tree stuff is done there is still some work to be done to get equippable items + perk-based items to be compatible with one another.

 

At this point I'm trying to get the bugs squashed out of new perk tree (nothing that crashes the game, just undesirable effects). After that I think I'll look into the equippable items (and the SHIV items, too).

Link to comment
Share on other sites

Regarding not being able to select the same perk multiple times, it's completely ok. I'm thinking an easy implementation of this dynamic inventory could be done just making armor grants 0 slots, soldiers get 1 slot when getting a class assigned (SetClass function) and later make deep pockets the only choice in some higher rank, like the sole option in rank 6 (using default trees). And as for those perks that give free items, in any case I was planning to make a unit can select them more than once each. That isn't a feature I'm looking for, I was just considering it as a possible implementation in case you'd code it soon; if it isn't in your plans don't do it just because of this.

 

I'm currently trying to code the spendable items solution I posted here, which actually involves rewritting entirely the XGAbilityTree.ApplyActionCost function. I'm still gathering pieces of code, but I think space won't be a problem, current code can be squeezed a lot. But the approach I'm using has some limitations, first is that it doesn't allow 2 items performing the same action, so that weaker medikit couldn't be implemented as a consumible item with this method. Second, it is assumed that every item grants 1 charge, so using the ability will use up the item; and third, in case the unit had extra items given by some perk, first the unit would spend those in the inventory, and use the free items last.

 

Actualy regarding different items performing the same ability the issue is a little bit more subtle, because we can actualy know the weapon that activated it, but there wouldn't be any way for the player to tell which item s/he's using. I'll think of some way to add a double check for two-version items, anyway I think I can live with those limitations, and in conjunction with this crude dynamic inventory implementation I've described here I think the result may be pretty good.

Edited by anUser
Link to comment
Share on other sites

Making some progress here with consumible items. I've got (almost) all the code written, but there's a couple of things I couldn't solve, so I'd like to ask our modders if they can help.

 

Code:

 

local int iCost;

if(kAbility.HasProperty(1) || kAbility.GetType() == 40 || kAbility.GetType() == 40)    // property 1: fire weapon; ablity 40: ghost; reserved space for kAbility.GetType() == 45  >> Reload
{
    iCost = 0;
    switch(kAbility.GetType())
    {
        case 22:
            kAbility.kUnit.m_iFragGrenades -= 1;
            iCost = 85;
        case 24:
            kAbility.kUnit.m_iAlienGrenades -= 1;
            iCost = 88;
        case 36:
        case 37:
        case 41:
            kAbility.kUnit.m_iMediKitCharges -= 1;
            iCost = 76;
        case 66:
            kAbility.kUnit.m_iShredderRockets -= 1;
            iCost = 87;    // Flasbang Grenade
        case 25:
            kAbility.kUnit.m_iRockets -= 1;
            if (kAbility.m_kWeapon == 7) {        // if it doesn't work try kAbility.m_kWeapon.AllObjects('XGWeapon_RocketLauncher') ?? byte/int casting??
                iCost = 89;    // eItem_PlaceholderGrenade0    ;rocket
            } else {
                iCost = 90;    // eItem_PlaceholderGrenade1    ;plasma rocket
            }
        case 40:
            kAbility.kUnit.m_iGhostCharges -= 1;
        case 23:
            kAbility.kUnit.m_iSmokeGrenades -= 1;
            iCost = 86;
        case 71:
            kAbility.kUnit.m_iBattleScanners -= 1;
            iCost = 99;
    }

    // Null bytes here to further add more abilities

    if (kAbility.m_kWeapon != none && kAbility.m_kWeapon.AllObjects('XGWeapon_ArcThrower')) {
        kAbility.kUnit.m_iArcThrowerCharges -= 1;
        iCost = 92;    // eItem_SectoidGrenade        ;arc thrower battery
    }

    if (iCost)
    {
        kAbility.kUnit.m_iAlienGrenades += kAbility.kUnit.m_iFragGrenades*16;
        kAbility.kUnit.m_iFragGrenades = iCost;  // stores item ID
        iCost = 0;
        // implied jump
        if (iCost < kAbility.kUnit.m_kCharacter.m_kChar.m_kInventory.arrSmallItems) {
    
            if (kAbility.kUnit.m_kCharacter.m_kChar.m_kInventory.arrSmallItems[iCost] == kAbility.kUnit.m_iFragGrenades) {
            {
 ?!?                (XGFacility_Lockers).UnequipSmallItem(XGStrategySoldier kSoldier, int iSlot);    ??
                    >> (XGStorage).RelseaseItem(kAbility.kUnit.iFragGrenades, kAbility.m_kUnit.m_kCharacter);
                    >> kAbility.m_kUnit.m_kCharacter.m_kChar.m_kInventory.TInventorySmallItemsSetItem(kAbility.m_kUnit.m_kCharacter.m_kChar.m_kInventory, iCost, 0)
 ?!?                (XGStorage).RemoveItem(kAbility.kUnit.iFragGrenades, 1);
                break;
            
            }
            iCost++;
            jump back;
        }
        kAbility.kUnit.m_iFragGrenades = int(kAbility.kUnit.iAlienGrenades / 16);
        kAbility.kUnit.m_iAlienGrenades =% 16;
    }

    if(kAbility.m_kWeapon != none)
    {
        iCost = XComGameReplicationInfo(class'Engine'.static.GetCurrentWorldInfo().GRI).m_kGameCore.GetAmmoCost(kAbility.m_kWeapon.GameplayType(), kAbility.GetType(), kAbility.m_kUnit.GetPlayer().HasFoundryHistory(10), kAbility.m_kUnit.GetCharacter().m_kChar, kAbility.m_bReactionFire);
        kAbility.m_kWeapon.ApplyAmmoCost(iCost);
    }
}

 

The problems I'm hitting are:

 

- getting kAbility.kUnit.m_iFragGrenades and other XGUnit inherited variables. I'm using as a template the kAbility.m_kUnit.m_bInfiniteGrenades call, replacing the 2D (boolVar) for 2C (int)... but I couldn't find the reference to the variables. I've looked for them but they aren't "called" from XGAbilityTree... so, do you ppl know some trick to know beforehand an instance variable reference code like there is for functions?

 

- I need to call a function in XGStorage from XGAbilityTree.ApplyActionCost but I have no idea how to do that...

 

- I could use a call to XGFacility_Lockers.UnequipSmallItem, because that function calls ReleaseItem and TInventorySmallItemsSetItem, but it requires as argument an XGStrategySoldier, and I'm not sure it can be retrieved from there or how.

 

Any tip or hint is much appreciated

Link to comment
Share on other sites

1) Variables that are defined as a member of a class always use the same reference to access the member variable. You can tell these references because they start with 01. m_iFragGrenades will always use the same reference (from anywhere within xcomgame.upk). Just find the four-digit value anywhere else in any function and use the same value when constructing kAbility.m_kUnit.m_iFragGrenades (I'm pretty sure it should be m_kUnit there and not kUnit, if they followed their naming conventions). The m_ typically indicates that it a class variable (as opposed to a local variable).

 

2) You are totally stuck here. XGStorage is a class defined in XcomStrategyGame.upk, whereas XGAbilityTree is a class in XcomGame.upk. As far as I've been able to determine, it is completely 100% impossible to call strategygame functions from the tacticalgame. The strategygame can call some tacticalgame functions, however.

 

I think this is because the tactical game was built well before the strategy game, so the tactical game is the "core" program.

 

3) Same as in 2. XGFacility_Lockers is a strategy class, while XGAbilityTree (and XGAbility) are tactical classes, so you can't access XGFacility_Lockers.

 

-----------------------

 

I'm sorry about the unfortunate news that tactical game code can't access strategy game code. This is what made getting the strategy game date/time so challenging.

 

For reference, even though there are functions STAT_GetStat() in both the tactical and strategy games, these are NOT the same functions, nor are they working directly on the same data. All of the RecapData information (the master copy is stored in the strategy game) is copied into the StrategyGameTransport when a mission is launched.

 

The STAT_GetStat() function in the strategy game retrieves from the master copy.

 

The STAT_GetStat() function in the tactical game retrieves from the copy stored in StrategyGameTransport

 

The STAT_SetStat() function in the tactical game stores into the copy stored in the StrategyGameTransport

 

One a mission is finished, the StrategyGameTransport is updated with all relevant data (such as from CollectArtifactsFromDeadAliens), then control is sent back to the strategy game.

 

The class StrategyGameTransport is actually defined in XcomGame.upk. Many XcomGame.upk functions are linked into XcomStrategyGame.upk (hence those tactical functions can be called from the strategy code), but the reverse is not true. This is how the strategy game accesses the StrategyGameTransport. As far as I can tell it is the only method by which information from the strategy game is made available to the tactical game.

 

In constrast, the strategy game often retrieves info from the tactical game code --- all of the DGC.ini variables are stored in XcomGame.upk, as well as all of the perk information, and the UIUitilities, which translates EImageIDs from numbers into the strings used in the SWF code to retrieve 2D graphics.

 

However, once a mission is finished, the XGBattle_SP class instance for that battle is garbage collected -- this means all of the units get cleaned up as well. So by the time the code in XcomStrategyGame.upk is executing you can no longer access the XGUnit information.

 

--------------------------------------------------------

 

I think the way around the restrictions in 2 and 3 is the following:

 

1) In the tactical game, restrict yourself to only editing the unit's inventory as set in kAbility.m_kUnit.m_kCharacter.m_kChar.kInventory ((note: kInventory breaks the 'm_' naming convention because it is a structure member, not a class member))

 

2) Functions such as TInventorySmallItemsSetItem can be used because they are defined in XGTacticalGameCore, which is a part of XcomGame.upk

 

3) Unit TCharacter information (the variable m_kChar in (1)) is passed back to the strategy game via the StrategyGameTransport class.

 

4) Access the TCharacter kInventory when UnloadSoldier is called back in XcomStrategyGame.upk, and use that information to update items in XGStorage.

Edited by Amineri
Link to comment
Share on other sites

This are indeed terrible news, but it's good to know how it works and what are the limitations.

I'll try to think of some way to remove the items later in the strategic game, the problem is how to tell which item to remove if it's no longer present in the inventory...

 

I'm thinking maybe there's a way to substract from the recovered items array so in the end of the mission it showed negative values for the items you've used, if that is then mapped/added to the storage array it may be a way to go "on the fly" without editting strategic game, if that's possible, or some workaround of the sort, making soldiers carry a negative amount of items. I recall seeing some code to check for multiple items *inside* one single inventory slot, not sure if there's anything workable of it.

Link to comment
Share on other sites

  • Recently Browsing   0 members

    • No registered users viewing this page.

×
×
  • Create New...