Jump to content

Partial solution: Take All Unequipped


ThoraldGM

Recommended Posts

Posting this for collaboration. A take all from companion command that leaves equipped items alone has been requested by multiple people, myself included. I dug into ContainerMenu.swf and found a lot of relevant code, but I have zero experience editing swf files. (Once edited, how are they recompiled? Is the original file altered, or is it overridden like a mod?) I used the ShowMyCode website to read the swf file.

 

Here's the TLDR:

if(this.BGSCodeObj.getSelectedItemEquipped(index, inContainer)) // leave equipped item alone
  else // move this unequipped item to the player

// repeat for each item in companion's inventory (array?)

// *** EDIT: More elegant, easier version ***

if(!(this.BGSCodeObj.getSelectedItemEquipped(index, inContainer))) // item is unequipped, move it
// repeat for each item in companion's inventory

The source path/file is:

 

Bethesda Archive Extractor > Interface.ba2 > Interface > ContainerMenu.swf > ShowMyCode site > ContainerMenu.txt

 

 

 

The rest of the nuggets in the file are below. This is a list of snippets, not the actual code file (which would be 5K+ lines of pasting, so snippets it is).

 

IMPORTANT: There is a reference to takeAllItems() in a new Object(). I'm guessing that's where the (for loop?) would be that handles iteration through the companion's inventory array. But I don't have read access to that, afaik.

 

 

 

 

public class ContainerMenu extends IMenu {
    protected var TakeAllButton:BSButtonHintData;


public function ContainerMenu(){
this.TakeAllButton = new BSButtonHintData("$TAKE ALL", "R Alt", "PSN_X", "Xenon_X", 1, this.onTakeAll);


public function onTakeAll(){
    this.BGSCodeObj.takeAllItems();


public class BGSExternalInterface {


        public static function call(BGSCodeObj:Object, ... _args):void{
            var funcName:String;
            var func:Function;
            if (BGSCodeObj != null){
                funcName = _args.shift();
                func = BGSCodeObj[funcName];
                if (func != null){
                    func.apply(null, _args);
                } else {
                    trace((("BGSExternalInterface::call -- Can't call function '" + funcName) + "' on BGSCodeObj. This function doesn't exist!"));
                };
            } else {
                trace((("BGSExternalInterface::call -- Can't call function '" + funcName) + "' on BGSCodeObj. BGSCodeObj is null!"));
            };
        }


}


public class ContainerMenu extends IMenu {
    public var BGSCodeObj:Object;


       public function ContainerMenu(){
           this.BGSCodeObj = new Object();


protected function UpdateButtonHints():void{


            if (((this.EquipOrStoreButton.ButtonVisible) && (!(modalMenuIsActive)))){
                index = (stage.focus as ItemList).selectedIndex;
                inContainer = (stage.focus == this.ContainerList_mc);
                this.EquipOrStoreButton.ButtonVisible = this.BGSCodeObj.getSelectedItemEquippable(index, inContainer);
                this.EquipOrStoreButton.ButtonText = ((this.BGSCodeObj.getSelectedItemEquipped(index, inContainer)) ? "$UNEQUIP" : "$EQUIP");
            }


        private function onItemPress(event:Event){
            var iitemCount:int;
            var iitemValue:*;
            if (visible){
                if (this.InspectingFeaturedItem){
                    this.InspectingFeaturedItem = false;
                } else {
                    iitemCount = (event.target as ItemList).selectedEntry.count;
                    if ((event.target as ItemList).selectedEntry.suppressQuantityMenu == true){
                        this.BGSCodeObj.transferItem((event.target as ItemList).selectedIndex, iitemCount, (event.target == this.ContainerList_mc));
                        this.BlockNextListFocusSound = true;
                        this.onTransferItem((((stage.focus == this.PlayerInventory_mc.PlayerList_mc)) ? this.ContainerList_mc : this.PlayerInventory_mc.PlayerList_mc));
                    } else {
                        if (iitemCount <= QuantityMenu.INV_MAX_NUM_BEFORE_QUANTITY_MENU){
                            this.BGSCodeObj.transferItem((event.target as ItemList).selectedIndex, 1, (event.target == this.ContainerList_mc));
                            this.BlockNextListFocusSound = true;
                            this.onTransferItem((((stage.focus == this.PlayerInventory_mc.PlayerList_mc)) ? this.ContainerList_mc : this.PlayerInventory_mc.PlayerList_mc));
                        } else {
                            iitemValue = this.BGSCodeObj.getItemValue((event.target as ItemList).selectedIndex, (event.target == this.ContainerList_mc));
                            this.OpenQuantityMenu(iitemCount, iitemValue);
                        };
                    };
                };
            };
        }

 

 

 

Zarchell and I researched this as part of our Spring Break "Mod Madness" project. I don't think we'll find a solution on our own this week, so it's up for grabs if anyone wants to run with it and publish. If you know the rest of the puzzle, please share and/or post here so we can learn.

 

At least we know some of the function names now, so we can research or build from that. Any swf gurus here?

Link to comment
Share on other sites

I've done Actionscript coding before so I can read most of the code here, although I'm a little rusty.

 

This is the right bit here:

 

this.BGSCodeObj.takeAllItems();

 

It's a method call. Let me see if I can track down the method and see how it works.

 

Edit: Oh sorry, I see what you mean here about not having access. The BGSCodeObj references seems to be part of Fallout 4, rather than contained in any of the .swf. That will make this a pain.

Edited by PoliteRaider
Link to comment
Share on other sites

Okay, so first thing first. The .swf is a compiled version of the .fla file that flash developer environments use. You would take this code, edit it, recompile it through something like FlashDevelop and then place the exported .swf back into the data/interface directory where it will override the version from the archive.

 

Unless I'm mistaken (which is quite possible) we can't access the takeAllItems() method at all with our present tools to edit how that works.

 

However we can potentially edit the OnTakeAll function that normally just calls takeAllItems and instead tell it to individually cycle through the items and send a take request for each one maybe? I'll need to look at this again when I'm more awake. It might be possible though I'm going to struggle to remember enough action script to manage coding that, it's been a while.

 

Edit: ... which is basically what you were suggesting already. Sorry, I had to go the long way around to get to the same place.

 

In other words, yes. That should work. You've tracked down the right bits and it's just a matter of getting the loop through the ContainerList_mc array and then sending the command for each. I'll try to figure out the correct loop code when I'm a bit more awake. In the meantime I'm off to bed.

 

Nice find.

Edited by PoliteRaider
Link to comment
Share on other sites

You can use JPEXS to edit the .swf files and there code. I've done simple edits to the menus just changing the on screen key names to match my F4SE custom control map.

 

I don't know if this will help but you can recompile the scripts with JPEXS and view everything in the .swf files. I'm a total noob when it comes to .swf's so this is about all I have for you. :blush:

 

Edit:

 

 

 

You would take this code, edit it, recompile it through something like FlashDevelop and then place the exported .swf back into the data/interface directory where it will override the version from the archive.

 

Yes your edited .swf files need to be placed in the data folder just like any texture mesh, replacer.

 

Edit #2:

 

When using JPEXS to edit the scripts in the .swf file you will see two edit boxes while viewing the script use the one on the far right to edit the script. I have no clew why but if you use the one in the middle the edit will cause a CTD when using the new .swf file in the game. The Right side edit seems to work though. (If that makes any sense again I'm a noob when it comes to .swf's)

Edited by chucksteel
Link to comment
Share on other sites

Nice. I think we're all on the right track. Need to know how many items in companion inventory (sizeof? for each?) and whether moving item is silent or prints message to top left of screen. I don't like guessing what's in takeAllItems, but if we can revise onTakeAll without ill behavior then that will have to do.

 

Burning the candle at both ends, so will take a fresh look later. I'm pretty sure the syntax from the TLDR at top is right. Just need to insert the second (if(!..)) condition check before the transfer of item, and the loop we create will rinse and repeat until sizeof is met.

Link to comment
Share on other sites

JPEXS is awesome. Still trying to isolate the name of the container array, then the syntax of the for loop (if that's even what ActionScript uses). Plus will need to check if container is not a companion first.

 

function onTakeAll

If not a companion, takeAllItems

else do our stuff instead

 

Sorry for the pseudo code, typing on phone.

Link to comment
Share on other sites

Well, I took a shot at this. And I've come to the conclusion that I hate Flash. Even more than I already did.

 

So, here is (roughly) the code that would be needed to make this happen:

 

      public function onTakeAll() : *
      {
            if (this.uiMode == CM_TEAMMATE)
            {
                  var i:* = 0;
                  var iitemCount:* = 0;
                  for(i = 0; i < (this.ContainerList_mc as ItemList).numListItems; i++)            
                  {
                        iitemCount = (this.ContainerList_mc as ItemList).EntriesA[i].count;
                        if (!(this.BGSCodeObj.getSelectedItemEquipped((this.ContainerList_mc as ItemList).EntriesA[i], true)))
                        {
                              this.BGSCodeObj.transferItem((this.ContainerList_mc as ItemList).EntriesA[i],iitemCount,true);
                              this.BlockNextListFocusSound = true;
                              this.onTransferItem(stage.focus == this.PlayerInventory_mc.PlayerList_mc?this.ContainerList_mc:this.PlayerInventory_mc.PlayerList_mc);        
                        }
                  }                
            } else {
                  this.BGSCodeObj.takeAllItems();
            }
      }
The problem, of course, is that I have absolutely no idea how to make Flash stuff. I got as far as using JPEXS to open up the file and slip in that bit of code, but FO4 crashed immediately when I tried to open up the menu. So I then downloaded Flash Animate CC (or whatever it is called) and used JPEXS to decompile the SWF file into an FLA. And that's roughly where I'm sitting right now, because I'm way out of my depth when it comes to Flash.
Link to comment
Share on other sites

the only thing I do know for sure is where to edit the script to cause a crash. :laugh:

 

http://forums.nexusmods.com/public/style_images/underground/attachicon.gifJPEXS.jpg

Yes, I tried fiddling around with that, but I really have no idea how that 2nd pane works. It's like some kind of messed-up Assembly.

Link to comment
Share on other sites

  • Recently Browsing   0 members

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