├── .gitattributes
├── .gitignore
├── LICENSE
├── README.md
├── archive
├── EquipmentEx.cpmodproj
├── custom_refs.txt
└── source
│ ├── archive
│ └── equipment_ex
│ │ ├── gui
│ │ └── wardrobe.inkwidget
│ │ └── localization
│ │ ├── ar-ar.json
│ │ ├── cz-cz.json
│ │ ├── de-de.json
│ │ ├── en-us.json
│ │ ├── es-es.json
│ │ ├── fr-fr.json
│ │ ├── it-it.json
│ │ ├── kr-kr.json
│ │ ├── pl-pl.json
│ │ ├── pt-br.json
│ │ ├── ru-ru.json
│ │ ├── tr-tr.json
│ │ └── zh-cn.json
│ └── resources
│ └── EquipmentEx.archive.xl
├── scripts
├── CompatibilityManager.reds
├── DevMode.reds
├── Facade.reds
├── InventoryHelper.reds
├── Migrations.reds
├── OutfitConfig.reds
├── OutfitEvents.reds
├── OutfitState.reds
├── OutfitSystem.reds
├── Overrides
│ ├── BackpackMainGameController.reds
│ ├── CraftingGarmentItemPreviewGameController.reds
│ ├── EquipmentSystem.reds
│ ├── InventoryItemDisplayController.reds
│ ├── InventoryItemModeLogicController.reds
│ ├── PhotoModePlayerEntityComponent.reds
│ ├── PopupsManager.reds
│ ├── QuestTrackerGameController.reds
│ ├── Stash.reds
│ ├── UIInventoryItem.reds
│ ├── UIInventoryItemsManager.reds
│ ├── WardrobeSetPreviewGameController.reds
│ ├── WardrobeUIGameController.reds
│ ├── gameuiInGameMenuGameController.reds
│ ├── gameuiInventoryGameController.reds
│ ├── gameuiPhotoModeMenuController.reds
│ ├── inkInventoryPuppetPreviewGameController.reds
│ └── inkScrollController.reds
├── PaperdollHelper.reds
├── Tweaks
│ ├── OutfitSlotMatcher.reds
│ ├── OutfitTweakHelper.reds
│ ├── PatchCustomItems.reds
│ ├── PatchOriginaltems.reds
│ └── RegisterOutfitSlots.reds
├── UI
│ ├── ArchivePopup.reds
│ ├── CollapseButton.reds
│ ├── ConflictsPopup.reds
│ ├── InventoryGridData.reds
│ ├── InventoryGridItem.reds
│ ├── InventoryGridSlot.reds
│ ├── ItemSourceOption.reds
│ ├── OutfitListData.reds
│ ├── OutfitListEntry.reds
│ ├── OutfitManager.reds
│ ├── OutfitMappingPopup.reds
│ ├── OutfitSlotOption.reds
│ ├── RequirementsPopup.reds
│ ├── SettingsButton.reds
│ ├── ViewSettingsPopup.reds
│ ├── WardrobeHubBtn.reds
│ ├── WardrobeHubLink.reds
│ └── WardrobeScreen.reds
├── ViewEvents.reds
├── ViewManager.reds
└── ViewState.reds
├── support
├── hints
│ └── EquipmentEx.toml
└── localization
│ └── lang.json.json
└── tools
└── dist
├── install-mod.ps1
├── package-mod.ps1
└── steps
├── compose-archives.ps1
├── compose-hints.ps1
├── compose-redscripts.ps1
├── create-zip-from-stage.ps1
├── get-version.ps1
└── install-from-stage.ps1
/.gitattributes:
--------------------------------------------------------------------------------
1 | *.reds linguist-language=swift
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /.dev
2 | /.idea
3 | /.vscode
4 | /archive/packed
5 | /archive/source/raw/**/*.json
6 | /archive/*.xml
7 | /archive/*.zip
8 | /archive/fileTreeState.json
9 | /build
10 | /dev
11 | /*.log
12 | ~$*
13 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Pavel Siberx
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Equipment-EX
2 |
3 | - Adds a new transmog system with 50+ clothing slots
4 | - Adds a brand-new UI accessible from Hub menu and V's apartments
5 | - Allows you to manage an unlimited amount of outfits with your names
6 | - Converts your existing wardrobe sets to a new system at a first launch
7 | - Works with vanilla and custom items
8 |
9 | ## Getting Started
10 |
11 | ### Requirements
12 |
13 | - Cyberpunk 2077 2.2
14 | - [ArchiveXL](https://github.com/psiberx/cp2077-archive-xl) 1.18.0+
15 | - [TweakXL](https://github.com/psiberx/cp2077-tweak-xl) 1.10.4+
16 | - [Codeware](https://github.com/psiberx/cp2077-codeware) 1.13.0+
17 | - [redscript](https://github.com/jac3km4/redscript) 0.5.25+
18 |
19 | ### Installation
20 |
21 | 1. Install requirements
22 | 2. Download [the latest release](https://github.com/psiberx/cp2077-equipment-ex/releases) archive
23 | 3. Extract the archive into the Cyberpunk 2077 installation directory
24 |
25 | ### How to use
26 |
27 | - The outfit manager is accessible through the new "Wardrobe" button in the Inventory menu or from wardrobe call in V's apartments
28 | - On the right side of the screen, you will see all compatible gear grouped by slots
29 | - Clicking on any item will activate outfit mode, which applies the visuals of the selected items to your character over equipped gear
30 | - On the left side of the screen, you will see a list of your outfits
31 | - The "Save outfit" button becomes available when outfit mode is active
32 | - To equip a previously saved outfit, just click on the name in the list
33 | - To delete an outfit, hover over the outfit and press the hotkey from the hint
34 | - To disable the outfit mode, you can select "No outfit" or unequip the outfit from the Inventory menu
35 | - In photo mode, you will find the option to change outfits on the fly in the pose section
36 |
37 | ## Documentation
38 |
39 | ### Compatible items
40 |
41 | Any item can support one base slot and one outfit slot at the same time,
42 | making the item compatible with both the game equipment system and the outfit system.
43 |
44 | The outfit slot must be added to the `placementSlots` property after the base slot:
45 |
46 | ```yaml
47 | Items.MyNecklace:
48 | $base: Items.GenericHeadClothing
49 | placementSlots:
50 | - !append OutfitSlots.NecklaceShort
51 | ```
52 |
53 | Which is equivalent to:
54 |
55 | ```yaml
56 | Items.MyNecklace:
57 | $base: Items.GenericHeadClothing
58 | placementSlots:
59 | - AttachmentSlots.Head
60 | - OutfitSlots.NecklaceShort
61 | ```
62 |
63 | If user doesn't have Equipment-EX installed, the item will still work with the base slot.
64 |
65 | ### Outfit slots
66 |
67 | | Slot name | Purpose |
68 | |:-------------------------------|:-------------------------------------------------|
69 | | `OutfitSlots.Head` | Hats, Hijabs, Helmets |
70 | | `OutfitSlots.Balaclava` | Balaclavas |
71 | | `OutfitSlots.Mask` | Face Masks |
72 | | `OutfitSlots.Glasses` | Glasses, Visors |
73 | | `OutfitSlots.Eyes` | Lenses, Visors |
74 | | `OutfitSlots.EyeLeft` | Lenses, Visors |
75 | | `OutfitSlots.EyeRight` | Lenses, Visors |
76 | | `OutfitSlots.Wreath` | Wreaths |
77 | | `OutfitSlots.EarLeft` | Earrings |
78 | | `OutfitSlots.EarRight` | Earrings |
79 | | `OutfitSlots.Neckwear` | Scarves, Collars |
80 | | `OutfitSlots.NecklaceTight` | Chokers |
81 | | `OutfitSlots.NecklaceShort` | Short Necklaces |
82 | | `OutfitSlots.NecklaceLong` | Long Necklaces |
83 | | `OutfitSlots.TorsoUnder` | Bras, Tight Long-sleeves |
84 | | `OutfitSlots.TorsoInner` | Tops, T-Shirts, Tight Shirts, Tight Dresses |
85 | | `OutfitSlots.TorsoMiddle` | Waistcoats, Blazers, Loose Shirts, Loose Dresses |
86 | | `OutfitSlots.TorsoOuter` | Jackets, Coats |
87 | | `OutfitSlots.TorsoAux` | Vests, Harnesses, Puffers |
88 | | `OutfitSlots.Back` | Backpacks, Swords |
89 | | `OutfitSlots.ShoulderLeft` | |
90 | | `OutfitSlots.ShoulderRight` | |
91 | | `OutfitSlots.ElbowLeft` | |
92 | | `OutfitSlots.ElbowRight` | |
93 | | `OutfitSlots.WristLeft` | Watches, Bands |
94 | | `OutfitSlots.WristRight` | Watches, Bands |
95 | | `OutfitSlots.Hands` | Gloves |
96 | | `OutfitSlots.HandLeft` | Gloves |
97 | | `OutfitSlots.HandRight` | Gloves |
98 | | `OutfitSlots.FingersLeft` | Rings |
99 | | `OutfitSlots.FingersRight` | Rings |
100 | | `OutfitSlots.FingernailsLeft` | Nails |
101 | | `OutfitSlots.FingernailsRight` | Nails |
102 | | `OutfitSlots.Waist` | Belts |
103 | | `OutfitSlots.LegsInner` | Tights, Leggings |
104 | | `OutfitSlots.LegsMiddle` | Tight Pants, Tight Shorts |
105 | | `OutfitSlots.LegsOuter` | Loose Pants, Loose Shorts, Skirts |
106 | | `OutfitSlots.ThighLeft` | |
107 | | `OutfitSlots.ThighRight` | |
108 | | `OutfitSlots.KneeLeft` | |
109 | | `OutfitSlots.KneeRight` | |
110 | | `OutfitSlots.AnkleLeft` | |
111 | | `OutfitSlots.AnkleRight` | |
112 | | `OutfitSlots.Feet` | Footwear |
113 | | `OutfitSlots.ToesLeft` | Accessories |
114 | | `OutfitSlots.ToesRight` | Accessories |
115 | | `OutfitSlots.ToenailsLeft` | Nails |
116 | | `OutfitSlots.ToenailsRight` | Nails |
117 | | `OutfitSlots.BodyUnder` | Netrunning Suits |
118 | | `OutfitSlots.BodyInner` | |
119 | | `OutfitSlots.BodyMiddle` | Jumpsuits, Tracksuits |
120 | | `OutfitSlots.BodyOuter` | |
121 | | `OutfitSlots.HandPropLeft` | Props for photo mode |
122 | | `OutfitSlots.HandPropRight` | Props for photo mode |
123 |
124 | When proposing a new slot, please follow these recommendations:
125 |
126 | - The purpose of the slot must be clear and distinguishable from other slots
127 | - The slot must represent an area or layer for only one item equipped at a time
128 | - The slot must be named after a body part if possible, or equipment category otherwise
129 |
130 | ### Auto conversions
131 |
132 | If you don't specify any outfit slot for your item,
133 | then the slot will be automatically assigned according to this table:
134 |
135 | | Base record | Assigned slot |
136 | |:----------------------------------|:-------------------------|
137 | | `Items.GenericHeadClothing` | `OutfitSlots.Head` |
138 | | `Items.Glasses` | `OutfitSlots.Glasses` |
139 | | `Items.Visor` | `OutfitSlots.Glasses` |
140 | | `Items.GenericFaceClothing` | `OutfitSlots.Mask` |
141 | | `Items.GenericInnerChestClothing` | `OutfitSlots.TorsoInner` |
142 | | `Items.GenericOuterChestClothing` | `OutfitSlots.TorsoOuter` |
143 | | `Items.GenericLegClothing` | `OutfitSlots.LegsMiddle` |
144 | | `Items.Skirt` | `OutfitSlots.LegsOuter` |
145 | | `Items.GenericFootClothing` | `OutfitSlots.Feet` |
146 | | `Items.Outfit` | `OutfitSlots.BodyMiddle` |
147 |
148 | ## Translations
149 |
150 | If you want to translate this mod into your language, get translation template JSON
151 | [`support/localization/lang.json.json`](https://github.com/psiberx/cp2077-equipment-ex/blob/master/support/localization/lang.json.json),
152 | translate all `femaleVariant` values, and send it to us.
153 | Please try to make a translation as close to the English version as possible.
154 | Your translation will be added to the mod, and you will be credited on the mod page.
155 |
156 | Available translations:
157 |
158 | - English (`en-us`)
159 | - Arabic (`ar-ar`) by [@MONSTERaider](https://www.nexusmods.com/users/1630457)
160 | - Brazilian (`pt-br`) by [@Jaqueta](https://github.com/Jaqueta)
161 | - Czech (`cz-cz`) by [@starfis](https://www.nexusmods.com/users/933641)
162 | - French (`fr-fr`) by [@TFE71](https://www.nexusmods.com/users/5620844)
163 | - German (`de-de`) by [@Vorgash](https://www.nexusmods.com/users/3957237)
164 | - Italian (`it-it`) by [@chipzz97](https://www.nexusmods.com/users/46275402)
165 | - Korean (`kr-kr`) by [@manikda11](https://www.nexusmods.com/users/47584948)
166 | - Polish (`pl-pl`) by [@gojowenus](https://www.nexusmods.com/users/5672133)
167 | - Russian (`ru-ru`) by [@Locked15](https://github.com/Locked15)
168 | - Simplified Chinese (`zh-cn`) by [@Zo70](https://www.nexusmods.com/users/158442118)
169 | - Spanish (`es-es`) by [@Anrui](https://www.nexusmods.com/users/36190195)
170 | - Turkish (`tr-tr`) by [@paielcitra](https://www.nexusmods.com/users/54660342)
171 |
--------------------------------------------------------------------------------
/archive/EquipmentEx.cpmodproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | EquipmentEx
4 | EquipmentEx
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/archive/custom_refs.txt:
--------------------------------------------------------------------------------
1 | equipment_ex\gui\wardrobe.inkwidget
2 | equipment_ex\localization\ar-ar.json
3 | equipment_ex\localization\cz-cz.json
4 | equipment_ex\localization\de-de.json
5 | equipment_ex\localization\en-us.json
6 | equipment_ex\localization\es-es.json
7 | equipment_ex\localization\fr-fr.json
8 | equipment_ex\localization\it-it.json
9 | equipment_ex\localization\kr-kr.json
10 | equipment_ex\localization\pl-pl.json
11 | equipment_ex\localization\pt-br.json
12 | equipment_ex\localization\ru-ru.json
13 | equipment_ex\localization\tr-tr.json
14 | equipment_ex\localization\zh-cn.json
15 |
--------------------------------------------------------------------------------
/archive/source/archive/equipment_ex/gui/wardrobe.inkwidget:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/psiberx/cp2077-equipment-ex/9c3990e758b663dd6dee752fc077f41eada431ce/archive/source/archive/equipment_ex/gui/wardrobe.inkwidget
--------------------------------------------------------------------------------
/archive/source/archive/equipment_ex/localization/ar-ar.json:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/psiberx/cp2077-equipment-ex/9c3990e758b663dd6dee752fc077f41eada431ce/archive/source/archive/equipment_ex/localization/ar-ar.json
--------------------------------------------------------------------------------
/archive/source/archive/equipment_ex/localization/cz-cz.json:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/psiberx/cp2077-equipment-ex/9c3990e758b663dd6dee752fc077f41eada431ce/archive/source/archive/equipment_ex/localization/cz-cz.json
--------------------------------------------------------------------------------
/archive/source/archive/equipment_ex/localization/de-de.json:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/psiberx/cp2077-equipment-ex/9c3990e758b663dd6dee752fc077f41eada431ce/archive/source/archive/equipment_ex/localization/de-de.json
--------------------------------------------------------------------------------
/archive/source/archive/equipment_ex/localization/en-us.json:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/psiberx/cp2077-equipment-ex/9c3990e758b663dd6dee752fc077f41eada431ce/archive/source/archive/equipment_ex/localization/en-us.json
--------------------------------------------------------------------------------
/archive/source/archive/equipment_ex/localization/es-es.json:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/psiberx/cp2077-equipment-ex/9c3990e758b663dd6dee752fc077f41eada431ce/archive/source/archive/equipment_ex/localization/es-es.json
--------------------------------------------------------------------------------
/archive/source/archive/equipment_ex/localization/fr-fr.json:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/psiberx/cp2077-equipment-ex/9c3990e758b663dd6dee752fc077f41eada431ce/archive/source/archive/equipment_ex/localization/fr-fr.json
--------------------------------------------------------------------------------
/archive/source/archive/equipment_ex/localization/it-it.json:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/psiberx/cp2077-equipment-ex/9c3990e758b663dd6dee752fc077f41eada431ce/archive/source/archive/equipment_ex/localization/it-it.json
--------------------------------------------------------------------------------
/archive/source/archive/equipment_ex/localization/kr-kr.json:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/psiberx/cp2077-equipment-ex/9c3990e758b663dd6dee752fc077f41eada431ce/archive/source/archive/equipment_ex/localization/kr-kr.json
--------------------------------------------------------------------------------
/archive/source/archive/equipment_ex/localization/pl-pl.json:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/psiberx/cp2077-equipment-ex/9c3990e758b663dd6dee752fc077f41eada431ce/archive/source/archive/equipment_ex/localization/pl-pl.json
--------------------------------------------------------------------------------
/archive/source/archive/equipment_ex/localization/pt-br.json:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/psiberx/cp2077-equipment-ex/9c3990e758b663dd6dee752fc077f41eada431ce/archive/source/archive/equipment_ex/localization/pt-br.json
--------------------------------------------------------------------------------
/archive/source/archive/equipment_ex/localization/ru-ru.json:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/psiberx/cp2077-equipment-ex/9c3990e758b663dd6dee752fc077f41eada431ce/archive/source/archive/equipment_ex/localization/ru-ru.json
--------------------------------------------------------------------------------
/archive/source/archive/equipment_ex/localization/tr-tr.json:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/psiberx/cp2077-equipment-ex/9c3990e758b663dd6dee752fc077f41eada431ce/archive/source/archive/equipment_ex/localization/tr-tr.json
--------------------------------------------------------------------------------
/archive/source/archive/equipment_ex/localization/zh-cn.json:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/psiberx/cp2077-equipment-ex/9c3990e758b663dd6dee752fc077f41eada431ce/archive/source/archive/equipment_ex/localization/zh-cn.json
--------------------------------------------------------------------------------
/archive/source/resources/EquipmentEx.archive.xl:
--------------------------------------------------------------------------------
1 | localization:
2 | onscreens:
3 | en-us: equipment_ex\localization\en-us.json
4 | ar-ar: equipment_ex\localization\ar-ar.json
5 | cz-cz: equipment_ex\localization\cz-cz.json
6 | de-de: equipment_ex\localization\de-de.json
7 | es-es: equipment_ex\localization\es-es.json
8 | fr-fr: equipment_ex\localization\fr-fr.json
9 | it-it: equipment_ex\localization\it-it.json
10 | kr-kr: equipment_ex\localization\kr-kr.json
11 | pl-pl: equipment_ex\localization\pl-pl.json
12 | pt-br: equipment_ex\localization\pt-br.json
13 | ru-ru: equipment_ex\localization\ru-ru.json
14 | tr-tr: equipment_ex\localization\tr-tr.json
15 | zh-cn: equipment_ex\localization\zh-cn.json
16 |
--------------------------------------------------------------------------------
/scripts/CompatibilityManager.reds:
--------------------------------------------------------------------------------
1 | module EquipmentEx
2 |
3 | public abstract class CompatibilityManager {
4 | public static func RequiredCodeware() -> String = "1.15.0";
5 | public static func RequiredArchiveXL() -> String = "1.21.0";
6 | public static func RequiredTweakXL() -> String = "1.10.7";
7 |
8 | public static func CheckRequirements() -> Bool {
9 | return Codeware.Require(CompatibilityManager.RequiredCodeware())
10 | && ArchiveXL.Require(CompatibilityManager.RequiredArchiveXL())
11 | && TweakXL.Require(CompatibilityManager.RequiredTweakXL());
12 | }
13 |
14 | public static func CheckConflicts(game: GameInstance, out conflicts: array) -> Bool {
15 | if IsDefined(GameInstance.GetScriptableSystemsContainer(game).Get(n"WardrobeSystemExtra")) {
16 | ArrayPush(conflicts, "Wardrobe Extras");
17 | }
18 |
19 | let dataManager = new InventoryDataManagerV2();
20 | dataManager.Initialize(GetPlayer(game));
21 | let questsSystem = GameInstance.GetQuestsSystem(game);
22 | let transmogEnabled = questsSystem.GetFact(n"transmog_enabled");
23 | questsSystem.SetFact(n"transmog_enabled", 7);
24 | if dataManager.IsTransmogEnabled() != 7 {
25 | ArrayPush(conflicts, "True Hidden Everything");
26 | }
27 | questsSystem.SetFact(n"transmog_enabled", transmogEnabled);
28 |
29 | let itemController = new InventoryItemDisplayController();
30 | itemController.SetLocked(true, true);
31 | if !itemController.m_isLocked {
32 | if itemController.m_visibleWhenLocked {
33 | ArrayPush(conflicts, "No Special Outfit Lock");
34 | } else {
35 | ArrayPush(conflicts, "Never Lock Outfits");
36 | }
37 | }
38 |
39 | if GameFileExists("archive/pc/mod/basegame_underwear_patch.archive") {
40 | ArrayPush(conflicts, "Underwear Remover by Sorrow446");
41 | }
42 |
43 | return ArraySize(conflicts) == 0;
44 | }
45 |
46 | public static func CheckConflicts(game: GameInstance) -> Bool {
47 | let conflicts: array;
48 | return CompatibilityManager.CheckConflicts(game, conflicts);
49 | }
50 |
51 | public static func IsUserNotified() -> Bool {
52 | return TweakDBInterface.GetBool(t"EquipmentEx.isUserNotified", false);
53 | }
54 |
55 | public static func MarkAsNotified() {
56 | TweakDBManager.SetFlat(t"EquipmentEx.isUserNotified", true);
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/scripts/DevMode.reds:
--------------------------------------------------------------------------------
1 | module EquipmentEx
2 |
3 | @if(ModuleExists("EquipmentEx.DevMode"))
4 | public static func DevMode() -> Bool = true;
5 |
6 | @if(!ModuleExists("EquipmentEx.DevMode"))
7 | public static func DevMode() -> Bool = false;
8 |
--------------------------------------------------------------------------------
/scripts/Facade.reds:
--------------------------------------------------------------------------------
1 | import EquipmentEx.OutfitSystem
2 |
3 | public abstract class EquipmentEx {
4 | public static func Version() -> String = "1.2.8";
5 |
6 | public static func Activate(game: GameInstance) {
7 | OutfitSystem.GetInstance(game).Activate();
8 | }
9 |
10 | public static func Reactivate(game: GameInstance) {
11 | OutfitSystem.GetInstance(game).Reactivate();
12 | }
13 |
14 | public static func Deactivate(game: GameInstance) {
15 | OutfitSystem.GetInstance(game).Deactivate();
16 | }
17 |
18 | public static func EquipItem(game: GameInstance, itemID: TweakDBID) {
19 | OutfitSystem.GetInstance(game).EquipItem(itemID);
20 | }
21 |
22 | public static func EquipItem(game: GameInstance, itemID: TweakDBID, slotID: TweakDBID) {
23 | OutfitSystem.GetInstance(game).EquipItem(itemID, slotID);
24 | }
25 |
26 | public static func UnequipItem(game: GameInstance, itemID: TweakDBID) {
27 | OutfitSystem.GetInstance(game).UnequipItem(itemID);
28 | }
29 |
30 | public static func UnequipSlot(game: GameInstance, slotID: TweakDBID) {
31 | OutfitSystem.GetInstance(game).UnequipSlot(slotID);
32 | }
33 |
34 | public static func UnequipAll(game: GameInstance) {
35 | OutfitSystem.GetInstance(game).UnequipAll();
36 | }
37 |
38 | public static func PrintItems(game: GameInstance) {
39 | let outfitSystem = OutfitSystem.GetInstance(game);
40 | let usedSlots = outfitSystem.GetUsedSlots();
41 |
42 | if ArraySize(usedSlots) > 0 {
43 | let transactionSystem = GameInstance.GetTransactionSystem(game);
44 | let player = GetPlayer(game);
45 |
46 | Print("=== Equipped Items ===");
47 |
48 | for slotID in usedSlots {
49 | let itemID = transactionSystem.GetItemInSlot(player, slotID).GetItemID();
50 | Print(s"\(outfitSystem.GetSlotName(slotID)) : \(outfitSystem.GetItemName(itemID))");
51 | }
52 |
53 | Print("===");
54 | } else {
55 | Print("=== No Equipped Items ===");
56 | }
57 | }
58 |
59 | public static func ExportItems(game: GameInstance) {
60 | let outfitSystem = OutfitSystem.GetInstance(game);
61 | let usedSlots = outfitSystem.GetUsedSlots();
62 |
63 | if ArraySize(usedSlots) > 0 {
64 | let transactionSystem = GameInstance.GetTransactionSystem(game);
65 | let player = GetPlayer(game);
66 | let command = "";
67 |
68 | for slotID in usedSlots {
69 | let itemID = transactionSystem.GetItemInSlot(player, slotID).GetItemID();
70 | command += s"EquipmentEx.EquipItem(\"\(TDBID.ToStringDEBUG(ItemID.GetTDBID(itemID)))\") ";
71 | }
72 |
73 | Print(command);
74 | }
75 | }
76 |
77 | public static func LoadOutfit(game: GameInstance, name: CName) {
78 | OutfitSystem.GetInstance(game).LoadOutfit(name);
79 | }
80 |
81 | public static func SaveOutfit(game: GameInstance, name: String) {
82 | OutfitSystem.GetInstance(game).SaveOutfit(StringToName(name), true);
83 | }
84 |
85 | public static func CopyOutfit(game: GameInstance, name: String, from: CName) {
86 | OutfitSystem.GetInstance(game).CopyOutfit(StringToName(name), from);
87 | }
88 |
89 | public static func DeleteOutfit(game: GameInstance, name: CName) {
90 | OutfitSystem.GetInstance(game).DeleteOutfit(name);
91 | }
92 |
93 | public static func DeleteAllOutfits(game: GameInstance) {
94 | OutfitSystem.GetInstance(game).DeleteAllOutfits();
95 | Print("All outfits deleted");
96 | }
97 |
98 | public static func PrintOutfits(game: GameInstance) {
99 | let outfitSystem = OutfitSystem.GetInstance(game);
100 | let outfitNames = outfitSystem.GetOutfits();
101 |
102 | if ArraySize(outfitNames) > 0 {
103 | Print("=== Saved Outfits ===");
104 |
105 | for outfitName in outfitNames {
106 | Print(NameToString(outfitName));
107 | }
108 |
109 | Print("===");
110 | } else {
111 | Print("=== No Saved Outfits ===");
112 | }
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/scripts/InventoryHelper.reds:
--------------------------------------------------------------------------------
1 | module EquipmentEx
2 |
3 | public class InventoryHelper extends ScriptableSystem {
4 | private let m_player: wref;
5 | private let m_transactionSystem: wref;
6 | private let m_wardrobeSystem: wref;
7 | private let m_inventoryManager: wref;
8 | private let m_stash: wref;
9 |
10 | private func OnPlayerAttach(request: ref) {
11 | this.m_player = GameInstance.GetPlayerSystem(this.GetGameInstance()).GetLocalPlayerMainGameObject();
12 | this.m_transactionSystem = GameInstance.GetTransactionSystem(this.GetGameInstance());
13 | this.m_wardrobeSystem = GameInstance.GetWardrobeSystem(this.GetGameInstance());
14 | this.m_inventoryManager = EquipmentSystem.GetData(this.m_player).GetInventoryManager();
15 | }
16 |
17 | private func IsValidItem(itemID: ItemID) -> Bool {
18 | let itemRecordId = ItemID.GetTDBID(itemID);
19 | let itemRecord = TweakDBInterface.GetClothingRecord(itemRecordId);
20 |
21 | return IsDefined(itemRecord);
22 | }
23 |
24 | public func GetStash() -> wref {
25 | return this.m_stash;
26 | }
27 |
28 | public func AddStash(stash: ref) {
29 | if !IsDefined(this.m_stash) {
30 | this.m_stash = stash;
31 | }
32 | }
33 |
34 | public func GetStashItems(out items: array) {
35 | let stashItems: array>;
36 | this.m_transactionSystem.GetItemList(this.m_stash, stashItems);
37 |
38 | for itemData in stashItems {
39 | if this.IsValidItem(itemData.GetID()) {
40 | ArrayPush(items, this.m_inventoryManager.GetCachedInventoryItemData(itemData));
41 | }
42 | }
43 | }
44 |
45 | public func GetPlayerItems(out items: array, opt excludes: array) {
46 | for itemData in this.m_inventoryManager.GetPlayerInventoryData() {
47 | let itemID = itemData.ID;
48 |
49 | if this.IsValidItem(itemID) {
50 | let diff = 0;
51 | for exclude in excludes {
52 | if Equals(exclude.itemID, itemID) {
53 | diff += exclude.quantity;
54 | }
55 | }
56 |
57 | if itemData.Quantity - diff > 0 {
58 | ArrayPush(items, itemData);
59 | }
60 | }
61 | }
62 | }
63 |
64 | public func GetWardrobeItems(out items: array) {
65 | for itemID in this.m_wardrobeSystem.GetStoredItemIDs() {
66 | if this.IsValidItem(itemID) {
67 | ArrayPush(items, this.m_inventoryManager.GetInventoryItemDataFromItemID(itemID));
68 | }
69 | }
70 | }
71 |
72 | public func GetAvailableItems(opt excludes: array) -> array {
73 | let items: array;
74 |
75 | switch ViewManager.GetInstance(this.GetGameInstance()).GetItemSource() {
76 | case WardrobeItemSource.InventoryOnly:
77 | this.GetPlayerItems(items, excludes);
78 | break;
79 | case WardrobeItemSource.InventoryAndStash:
80 | this.GetPlayerItems(items, excludes);
81 | this.GetStashItems(items);
82 | break;
83 | case WardrobeItemSource.WardrobeStore:
84 | this.GetWardrobeItems(items);
85 | break;
86 | }
87 |
88 | return items;
89 | }
90 |
91 | public func DiscardItem(itemID: ItemID) {
92 | switch ViewManager.GetInstance(this.GetGameInstance()).GetItemSource() {
93 | case WardrobeItemSource.InventoryOnly:
94 | this.m_transactionSystem.RemoveItem(this.m_player, itemID, 1);
95 | break;
96 | case WardrobeItemSource.InventoryAndStash:
97 | this.m_transactionSystem.RemoveItem(this.m_player, itemID, 1);
98 | this.m_transactionSystem.RemoveItem(this.m_stash, itemID, 1);
99 | break;
100 | case WardrobeItemSource.WardrobeStore:
101 | this.m_wardrobeSystem.ForgetItemID(itemID);
102 | break;
103 | }
104 | }
105 |
106 | public static func GetInstance(game: GameInstance) -> ref {
107 | return GameInstance.GetScriptableSystemsContainer(game).Get(n"EquipmentEx.InventoryHelper") as InventoryHelper;
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/scripts/Migrations.reds:
--------------------------------------------------------------------------------
1 | module EquipmentEx
2 |
3 | struct ExtractedSet {
4 | public let setID: Int32;
5 | public let clothingList: array;
6 | }
7 |
8 | @if(!ModuleExists("ExtraWardrobeSlots.Utils"))
9 | func ExtractClothingSets(game: GameInstance) -> array {
10 | let wardrobeSystem = GameInstance.GetWardrobeSystem(game);
11 | let clothingSets = wardrobeSystem.GetClothingSets();
12 | let extractedSets: array;
13 |
14 | for clothingSet in clothingSets {
15 | if ArraySize(clothingSet.clothingList) > 0 {
16 | ArrayPush(extractedSets, new ExtractedSet(
17 | EnumInt(clothingSet.setID) + 1,
18 | clothingSet.clothingList
19 | ));
20 | }
21 | }
22 |
23 | return extractedSets;
24 | }
25 |
26 | @if(ModuleExists("ExtraWardrobeSlots.Utils"))
27 | func ExtractClothingSets(game: GameInstance) -> array {
28 | let wardrobeSystem = WardrobeSystemExtra.GetInstance(game);
29 | let clothingSets = wardrobeSystem.GetClothingSets();
30 | let extractedSets: array;
31 |
32 | for clothingSet in clothingSets {
33 | if ArraySize(clothingSet.clothingList) > 0 {
34 | ArrayPush(extractedSets, new ExtractedSet(
35 | EnumInt(clothingSet.setID) + 1,
36 | clothingSet.clothingList
37 | ));
38 | }
39 | }
40 |
41 | return extractedSets;
42 | }
43 |
--------------------------------------------------------------------------------
/scripts/OutfitConfig.reds:
--------------------------------------------------------------------------------
1 | module EquipmentEx
2 |
3 | struct BaseSlotConfig {
4 | public let slotID: TweakDBID;
5 | public let equipmentArea: gamedataEquipmentArea;
6 |
7 | public static func Create(slotID: TweakDBID, equipmentArea: gamedataEquipmentArea) -> BaseSlotConfig {
8 | return new BaseSlotConfig(slotID, equipmentArea);
9 | }
10 | }
11 |
12 | struct ExtraSlotConfig {
13 | public let slotID: TweakDBID;
14 | public let slotName: CName;
15 | public let slotArea: CName;
16 | public let garmentOffset: Int32;
17 | public let relatedSlotIDs: array;
18 | public let dependencySlotIDs: array;
19 | public let displayName: String;
20 |
21 | public static func Create(slotArea: CName, slotName: CName, garmentOffset: Int32, opt relatedIDs: array, opt dependencyIDs: array) -> ExtraSlotConfig {
22 | return new ExtraSlotConfig(
23 | TDBID.Create(NameToString(slotName)),
24 | slotName,
25 | slotArea,
26 | garmentOffset,
27 | relatedIDs,
28 | dependencyIDs,
29 | "Gameplay-" + StrReplace(NameToString(slotName), ".", "-")
30 | );
31 | }
32 | }
33 |
34 | public abstract class OutfitConfig {
35 | public static func BaseSlots() -> array = [
36 | BaseSlotConfig.Create(t"AttachmentSlots.Head", gamedataEquipmentArea.Head),
37 | BaseSlotConfig.Create(t"AttachmentSlots.Eyes", gamedataEquipmentArea.Face),
38 | BaseSlotConfig.Create(t"AttachmentSlots.Chest", gamedataEquipmentArea.InnerChest),
39 | BaseSlotConfig.Create(t"AttachmentSlots.Torso", gamedataEquipmentArea.OuterChest),
40 | BaseSlotConfig.Create(t"AttachmentSlots.Legs", gamedataEquipmentArea.Legs),
41 | BaseSlotConfig.Create(t"AttachmentSlots.Feet", gamedataEquipmentArea.Feet),
42 | BaseSlotConfig.Create(t"AttachmentSlots.UnderwearTop", gamedataEquipmentArea.UnderwearTop),
43 | BaseSlotConfig.Create(t"AttachmentSlots.UnderwearBottom", gamedataEquipmentArea.UnderwearBottom)
44 | ];
45 |
46 | public static func OutfitSlots() -> array = [
47 | ExtraSlotConfig.Create(n"Head", n"OutfitSlots.Head", 310000, [t"AttachmentSlots.Head"]),
48 | ExtraSlotConfig.Create(n"Head", n"OutfitSlots.Balaclava", 160000, [t"AttachmentSlots.Head"]),
49 | ExtraSlotConfig.Create(n"Face", n"OutfitSlots.Mask", 170000, [t"AttachmentSlots.Eyes"]),
50 | ExtraSlotConfig.Create(n"Face", n"OutfitSlots.Glasses", 190000, [t"AttachmentSlots.Eyes"]),
51 | ExtraSlotConfig.Create(n"Face", n"OutfitSlots.Eyes", 130000, [t"AttachmentSlots.Eyes"]),
52 | ExtraSlotConfig.Create(n"Face", n"OutfitSlots.EyeLeft", 140000, [t"AttachmentSlots.Eyes"]),
53 | ExtraSlotConfig.Create(n"Face", n"OutfitSlots.EyeRight", 140000, [t"AttachmentSlots.Eyes"]),
54 | ExtraSlotConfig.Create(n"Face", n"OutfitSlots.Wreath", 180000, [t"AttachmentSlots.Eyes"]),
55 | ExtraSlotConfig.Create(n"Ears", n"OutfitSlots.EarLeft", 140000, [], [t"AttachmentSlots.Head"]),
56 | ExtraSlotConfig.Create(n"Ears", n"OutfitSlots.EarRight", 140000, [], [t"AttachmentSlots.Head"]),
57 | ExtraSlotConfig.Create(n"Neck", n"OutfitSlots.Neckwear", 200000, [], [t"AttachmentSlots.Head"]),
58 | ExtraSlotConfig.Create(n"Neck", n"OutfitSlots.NecklaceTight", 190000, [], [t"AttachmentSlots.Head"]),
59 | ExtraSlotConfig.Create(n"Neck", n"OutfitSlots.NecklaceShort", 190000),
60 | ExtraSlotConfig.Create(n"Neck", n"OutfitSlots.NecklaceLong", 190000),
61 | ExtraSlotConfig.Create(n"Torso", n"OutfitSlots.TorsoUnder", 120000, [t"AttachmentSlots.Chest"], [t"AttachmentSlots.Head", t"AttachmentSlots.Torso"]),
62 | ExtraSlotConfig.Create(n"Torso", n"OutfitSlots.TorsoInner", 150000, [t"AttachmentSlots.Chest"], [t"AttachmentSlots.Head", t"AttachmentSlots.Torso"]),
63 | ExtraSlotConfig.Create(n"Torso", n"OutfitSlots.TorsoMiddle", 180000, [t"AttachmentSlots.Torso"], [t"AttachmentSlots.Head"]),
64 | ExtraSlotConfig.Create(n"Torso", n"OutfitSlots.TorsoOuter", 210000, [t"AttachmentSlots.Torso"], [t"AttachmentSlots.Head"]),
65 | ExtraSlotConfig.Create(n"Torso", n"OutfitSlots.TorsoAux", 240000, [t"AttachmentSlots.Torso"], [t"AttachmentSlots.Head"]),
66 | ExtraSlotConfig.Create(n"Back", n"OutfitSlots.Back", 220000),
67 | ExtraSlotConfig.Create(n"Waist", n"OutfitSlots.Waist", 200000),
68 | ExtraSlotConfig.Create(n"Arms", n"OutfitSlots.ShoulderLeft", 200000),
69 | ExtraSlotConfig.Create(n"Arms", n"OutfitSlots.ShoulderRight", 200000),
70 | ExtraSlotConfig.Create(n"Arms", n"OutfitSlots.ElbowLeft", 200000),
71 | ExtraSlotConfig.Create(n"Arms", n"OutfitSlots.ElbowRight", 200000),
72 | ExtraSlotConfig.Create(n"Arms", n"OutfitSlots.WristLeft", 160000, [], [t"AttachmentSlots.Hands"]),
73 | ExtraSlotConfig.Create(n"Arms", n"OutfitSlots.WristRight", 160000, [], [t"AttachmentSlots.Hands"]),
74 | ExtraSlotConfig.Create(n"Hands", n"OutfitSlots.Hands", 160000, [], [t"AttachmentSlots.Hands"]),
75 | ExtraSlotConfig.Create(n"Hands", n"OutfitSlots.HandLeft", 170000, [], [t"AttachmentSlots.Hands"]),
76 | ExtraSlotConfig.Create(n"Hands", n"OutfitSlots.HandRight", 170000, [], [t"AttachmentSlots.Hands"]),
77 | ExtraSlotConfig.Create(n"Hands", n"OutfitSlots.HandPropLeft", 310000, [], [t"AttachmentSlots.Hands"]),
78 | ExtraSlotConfig.Create(n"Hands", n"OutfitSlots.HandPropRight", 310000, [], [t"AttachmentSlots.Hands"]),
79 | ExtraSlotConfig.Create(n"Fingers", n"OutfitSlots.FingersLeft", 180000, [], [t"AttachmentSlots.Hands"]),
80 | ExtraSlotConfig.Create(n"Fingers", n"OutfitSlots.FingersRight", 180000, [], [t"AttachmentSlots.Hands"]),
81 | ExtraSlotConfig.Create(n"Fingers", n"OutfitSlots.FingernailsLeft", 100000, [], [t"AttachmentSlots.Hands"]),
82 | ExtraSlotConfig.Create(n"Fingers", n"OutfitSlots.FingernailsRight", 100000, [], [t"AttachmentSlots.Hands"]),
83 | ExtraSlotConfig.Create(n"Legs", n"OutfitSlots.LegsInner", 130000, [t"AttachmentSlots.Legs"], [t"AttachmentSlots.Feet"]),
84 | ExtraSlotConfig.Create(n"Legs", n"OutfitSlots.LegsMiddle", 160000, [t"AttachmentSlots.Legs"], [t"AttachmentSlots.Feet"]),
85 | ExtraSlotConfig.Create(n"Legs", n"OutfitSlots.LegsOuter", 190000, [t"AttachmentSlots.Legs"], [t"AttachmentSlots.Feet"]),
86 | ExtraSlotConfig.Create(n"Legs", n"OutfitSlots.ThighLeft", 140000, [], [t"AttachmentSlots.Feet"]),
87 | ExtraSlotConfig.Create(n"Legs", n"OutfitSlots.ThighRight", 140000, [], [t"AttachmentSlots.Feet"]),
88 | ExtraSlotConfig.Create(n"Legs", n"OutfitSlots.KneeLeft", 140000, [], [t"AttachmentSlots.Feet"]),
89 | ExtraSlotConfig.Create(n"Legs", n"OutfitSlots.KneeRight", 140000, [], [t"AttachmentSlots.Feet"]),
90 | ExtraSlotConfig.Create(n"Legs", n"OutfitSlots.AnkleLeft", 140000, [], [t"AttachmentSlots.Feet"]),
91 | ExtraSlotConfig.Create(n"Legs", n"OutfitSlots.AnkleRight", 140000, [], [t"AttachmentSlots.Feet"]),
92 | ExtraSlotConfig.Create(n"Feet", n"OutfitSlots.Feet", 180000, [t"AttachmentSlots.Feet"]),
93 | ExtraSlotConfig.Create(n"Toes", n"OutfitSlots.ToesLeft", 120000, [], [t"AttachmentSlots.Feet"]),
94 | ExtraSlotConfig.Create(n"Toes", n"OutfitSlots.ToesRight", 120000, [], [t"AttachmentSlots.Feet"]),
95 | ExtraSlotConfig.Create(n"Toes", n"OutfitSlots.ToenailsLeft", 100000, [], [t"AttachmentSlots.Feet"]),
96 | ExtraSlotConfig.Create(n"Toes", n"OutfitSlots.ToenailsRight", 100000, [], [t"AttachmentSlots.Feet"]),
97 | ExtraSlotConfig.Create(n"Body", n"OutfitSlots.BodyUnder", 110000, [t"AttachmentSlots.Chest", t"AttachmentSlots.Legs"], [t"AttachmentSlots.Head", t"AttachmentSlots.Torso", t"AttachmentSlots.Feet"]),
98 | ExtraSlotConfig.Create(n"Body", n"OutfitSlots.BodyInner", 140000, [t"AttachmentSlots.Chest", t"AttachmentSlots.Legs"], [t"AttachmentSlots.Head", t"AttachmentSlots.Torso", t"AttachmentSlots.Feet"]),
99 | ExtraSlotConfig.Create(n"Body", n"OutfitSlots.BodyMiddle", 170000, [t"AttachmentSlots.Torso", t"AttachmentSlots.Legs"], [t"AttachmentSlots.Head", t"AttachmentSlots.Feet"]),
100 | ExtraSlotConfig.Create(n"Body", n"OutfitSlots.BodyOuter", 300000, [t"AttachmentSlots.Torso", t"AttachmentSlots.Legs"], [t"AttachmentSlots.Head", t"AttachmentSlots.Feet"])
101 | ];
102 | }
103 |
--------------------------------------------------------------------------------
/scripts/OutfitEvents.reds:
--------------------------------------------------------------------------------
1 | module EquipmentEx
2 |
3 | public class OutfitUpdated extends Event {
4 | public let isActive: Bool;
5 | public let outfitName: CName;
6 | }
7 |
8 | public class OutfitPartUpdated extends Event {
9 | public let itemID: ItemID;
10 | public let itemName: String;
11 | public let slotID: TweakDBID;
12 | public let slotName: String;
13 | public let isEquipped: Bool;
14 | }
15 |
16 | public class OutfitMappingUpdated extends Event {}
17 |
18 | public class OutfitListUpdated extends Event {}
19 |
--------------------------------------------------------------------------------
/scripts/OutfitState.reds:
--------------------------------------------------------------------------------
1 | module EquipmentEx
2 |
3 | public class OutfitPart {
4 | private persistent let m_itemID: ItemID;
5 | private persistent let m_slotID: TweakDBID;
6 |
7 | public func GetItemID() -> ItemID {
8 | return this.m_itemID;
9 | }
10 |
11 | public func GetItemHash() -> Uint64 {
12 | return ItemID.GetCombinedHash(this.m_itemID);
13 | }
14 |
15 | public func SetItemID(itemID: ItemID) {
16 | this.m_itemID = itemID;
17 | }
18 |
19 | public func GetSlotID() -> TweakDBID {
20 | return this.m_slotID;
21 | }
22 |
23 | public func SetSlotID(slotID: TweakDBID) {
24 | this.m_slotID = slotID;
25 | }
26 |
27 | public static func Create(itemID: ItemID, slotID: TweakDBID) -> ref {
28 | let instance = new OutfitPart();
29 | instance.m_itemID = itemID;
30 | instance.m_slotID = slotID;
31 | return instance;
32 | }
33 |
34 | public static func Clone(source: ref) -> ref {
35 | return OutfitPart.Create(source.m_itemID, source.m_slotID);
36 | }
37 | }
38 |
39 | class OutfitSet {
40 | private persistent let m_name: CName;
41 | private persistent let m_parts: array[>;
42 | private persistent let m_timestamp: Float;
43 | private let m_hash: Uint64;
44 |
45 | public func GetName() -> CName {
46 | return this.m_name;
47 | }
48 |
49 | public func SetName(name: CName) {
50 | this.m_name = name;
51 | }
52 |
53 | public func GetParts() -> array][> {
54 | return this.m_parts;
55 | }
56 |
57 | public func SetParts(parts: array][>) {
58 | ArrayResize(this.m_parts, ArraySize(parts));
59 |
60 | let i = 0;
61 | for part in parts {
62 | this.m_parts[i] = OutfitPart.Clone(part);
63 | i += 1;
64 | }
65 |
66 | this.UpdateHash();
67 | }
68 |
69 | public func GetHash() -> Uint64 {
70 | return this.m_hash;
71 | }
72 |
73 | public func UpdateHash() {
74 | this.m_hash = OutfitSet.MakeHash(this.m_parts);
75 | }
76 |
77 | public static func Create(name: CName, timestamp: Float, parts: array][>) -> ref {
78 | let instance = new OutfitSet();
79 | instance.m_name = name;
80 | instance.m_timestamp = timestamp;
81 | instance.SetParts(parts);
82 | return instance;
83 | }
84 |
85 | public static func Clone(name: CName, timestamp: Float, source: ref) -> ref {
86 | return OutfitSet.Create(name, timestamp, source.m_parts);
87 | }
88 |
89 | public static func MakeHash(parts: array][>) -> Uint64 {
90 | if ArraySize(parts) == 0 {
91 | return 0ul;
92 | }
93 |
94 | let items: array;
95 |
96 | for part in parts {
97 | let item = part.GetItemHash();
98 |
99 | let index = 0;
100 | while index < ArraySize(items) && items[index] < item {
101 | index += 1;
102 | }
103 |
104 | ArrayInsert(items, index, item);
105 | }
106 |
107 | let hash = 14695981039346656037ul; // 0xcbf29ce484222325
108 | let prime = 1099511628211ul; // 0x00000100000001B3
109 | let base = 256ul;
110 |
111 | for item in items {
112 | let i = 8;
113 | while i > 0 {
114 | hash = hash ^ (item % base);
115 | hash *= prime;
116 | item /= base;
117 | i -= 1;
118 | }
119 | }
120 |
121 | return hash;
122 | }
123 | }
124 |
125 | class OutfitState {
126 | private persistent let m_disabled: Bool;
127 | private persistent let m_active: Bool;
128 | private persistent let m_parts: array][>;
129 | private persistent let m_outfits: array][>;
130 | private persistent let m_mappings: array][>;
131 | private let m_hash: Uint64;
132 |
133 | public func IsDisabled() -> Bool {
134 | return this.m_disabled;
135 | }
136 |
137 | public func SetDisabled(state: Bool) {
138 | this.m_disabled = state;
139 | }
140 |
141 | public func IsActive() -> Bool {
142 | return this.m_active;
143 | }
144 |
145 | public func SetActive(state: Bool) {
146 | this.m_active = state;
147 | }
148 |
149 | public func GetParts() -> array][> {
150 | return this.m_parts;
151 | }
152 |
153 | public func HasPart(itemID: ItemID) -> Bool {
154 | return IsDefined(this.GetPart(itemID));
155 | }
156 |
157 | public func HasPart(slotID: TweakDBID) -> Bool {
158 | return IsDefined(this.GetPart(slotID));
159 | }
160 |
161 | public func GetPart(itemID: ItemID) -> ref {
162 | for part in this.m_parts {
163 | if Equals(part.GetItemID(), itemID) {
164 | return part;
165 | }
166 | }
167 | return null;
168 | }
169 |
170 | public func GetPart(slotID: TweakDBID) -> ref {
171 | for part in this.m_parts {
172 | if Equals(part.GetSlotID(), slotID) {
173 | return part;
174 | }
175 | }
176 | return null;
177 | }
178 |
179 | public func UpdatePart(itemID: ItemID, slotID: TweakDBID) {
180 | let updated = false;
181 |
182 | for part in this.m_parts {
183 | if Equals(part.GetItemID(), itemID) {
184 | if Equals(part.GetSlotID(), slotID) {
185 | return;
186 | }
187 | part.SetSlotID(slotID);
188 | updated = true;
189 | break;
190 | }
191 | }
192 |
193 | for part in this.m_parts {
194 | if Equals(part.GetSlotID(), slotID) {
195 | if updated {
196 | if NotEquals(part.GetItemID(), itemID) {
197 | ArrayRemove(this.m_parts, part);
198 | }
199 | } else {
200 | part.SetItemID(itemID);
201 | updated = true;
202 | }
203 | break;
204 | }
205 | }
206 |
207 | if !updated {
208 | ArrayPush(this.m_parts, OutfitPart.Create(itemID, slotID));
209 | }
210 |
211 | this.UpdateHash();
212 | }
213 |
214 | public func RemovePart(itemID: ItemID) -> Bool {
215 | for part in this.m_parts {
216 | if Equals(part.GetItemID(), itemID) {
217 | ArrayRemove(this.m_parts, part);
218 | this.UpdateHash();
219 | return true;
220 | }
221 | }
222 | return false;
223 | }
224 |
225 | public func RemovePart(slotID: TweakDBID) -> Bool {
226 | for part in this.m_parts {
227 | if Equals(part.GetSlotID(), slotID) {
228 | ArrayRemove(this.m_parts, part);
229 | this.UpdateHash();
230 | return true;
231 | }
232 | }
233 | return false;
234 | }
235 |
236 | public func ClearParts() {
237 | ArrayClear(this.m_parts);
238 |
239 | this.UpdateHash();
240 | }
241 |
242 | public func GetOutfits() -> array][> {
243 | return this.m_outfits;
244 | }
245 |
246 | public func GetOutfit(name: CName) -> ref {
247 | for outfit in this.m_outfits {
248 | if Equals(outfit.GetName(), name) {
249 | return outfit;
250 | }
251 | }
252 | return null;
253 | }
254 |
255 | public func SaveOutfit(name: CName, overwrite: Bool, timestamp: Float) -> Bool {
256 | return this.SaveOutfit(name, this.m_parts, overwrite, timestamp);
257 | }
258 |
259 | public func SaveOutfit(name: CName, parts: array][>, overwrite: Bool, timestamp: Float) -> Bool {
260 | let outfit = this.GetOutfit(name);
261 |
262 | if IsDefined(outfit) {
263 | if !overwrite {
264 | return false;
265 | }
266 |
267 | outfit.SetParts(parts);
268 | return true;
269 | }
270 |
271 | ArrayPush(this.m_outfits, OutfitSet.Create(name, timestamp, parts));
272 | return true;
273 | }
274 |
275 | public func CopyOutfit(name: CName, from: CName, timestamp: Float) -> Bool {
276 | let outfit = this.GetOutfit(name);
277 |
278 | if !IsDefined(outfit) {
279 | return false;
280 | }
281 |
282 | ArrayPush(this.m_outfits, OutfitSet.Clone(name, timestamp, outfit));
283 | return true;
284 | }
285 |
286 | public func DeleteOutfit(name: CName) -> Bool {
287 | let outfit = this.GetOutfit(name);
288 |
289 | if !IsDefined(outfit) {
290 | return false;
291 | }
292 |
293 | ArrayRemove(this.m_outfits, outfit);
294 | return true;
295 | }
296 |
297 | public func DeleteAllOutfits() -> Bool {
298 | if ArraySize(this.m_outfits) > 0 {
299 | ArrayClear(this.m_outfits);
300 | return true;
301 | }
302 |
303 | return false;
304 | }
305 |
306 | public func IsOutfit(name: CName) -> Bool {
307 | let outfit = this.GetOutfit(name);
308 |
309 | return IsDefined(outfit) ? this.m_hash == outfit.GetHash() : false;
310 | }
311 |
312 | public func IsOutfit(hash: Uint64) -> Bool {
313 | return this.m_hash == hash;
314 | }
315 |
316 | public func GetMappings() -> array][> {
317 | return this.m_mappings;
318 | }
319 |
320 | public func UpdateMapping(itemID: ItemID, slotID: TweakDBID) {
321 | let updated = false;
322 |
323 | for mapping in this.m_mappings {
324 | if Equals(mapping.GetItemID(), itemID) {
325 | if Equals(mapping.GetSlotID(), slotID) {
326 | return;
327 | }
328 | mapping.SetSlotID(slotID);
329 | updated = true;
330 | break;
331 | }
332 | }
333 |
334 | if !updated {
335 | ArrayPush(this.m_mappings, OutfitPart.Create(itemID, slotID));
336 | }
337 | }
338 |
339 | public func UpdateHash() {
340 | this.m_hash = OutfitSet.MakeHash(this.m_parts);
341 | }
342 |
343 | public func Restore() {
344 | this.UpdateHash();
345 |
346 | for outfit in this.m_outfits {
347 | outfit.UpdateHash();
348 | }
349 | }
350 |
351 | public static func Create() -> ref {
352 | return new OutfitState();
353 | }
354 | }
355 |
--------------------------------------------------------------------------------
/scripts/Overrides/BackpackMainGameController.reds:
--------------------------------------------------------------------------------
1 | import EquipmentEx.OutfitSystem
2 |
3 | @addField(BackpackMainGameController)
4 | private let m_outfitSystem: wref;
5 |
6 | @wrapMethod(BackpackMainGameController)
7 | protected cb func OnInitialize() -> Bool {
8 | wrappedMethod();
9 |
10 | this.m_outfitSystem = OutfitSystem.GetInstance(this.GetPlayerControlledObject().GetGame());
11 | }
12 |
13 | @wrapMethod(BackpackMainGameController)
14 | protected cb func OnItemDisplayClick(evt: ref) -> Bool {
15 | if this.m_outfitSystem.IsActive() && evt.actionName.IsAction(n"preview_item") {
16 | if evt.uiInventoryItem.IsClothing() {
17 | return false;
18 | }
19 | }
20 |
21 | return wrappedMethod(evt);
22 | }
23 |
24 | @wrapMethod(BackpackMainGameController)
25 | private final func NewShowItemHints(itemData: wref) {
26 | wrappedMethod(itemData);
27 |
28 | if this.m_outfitSystem.IsActive() {
29 | if itemData.IsClothing() {
30 | this.m_buttonHintsController.RemoveButtonHint(n"preview_item");
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/scripts/Overrides/CraftingGarmentItemPreviewGameController.reds:
--------------------------------------------------------------------------------
1 | import EquipmentEx.OutfitSystem
2 |
3 | @wrapMethod(CraftingGarmentItemPreviewGameController)
4 | protected cb func OnCrafrtingPreview(evt: ref) -> Bool {
5 | if this.m_outfitSystem.IsActive() {
6 | if ItemID.IsValid(this.m_previewedItem) {
7 | this.m_previewedItem = ItemID.None();
8 | this.m_outfitSystem.EquipPuppetOutfit(this.GetGamePuppet());
9 | }
10 |
11 | if evt.isGarment {
12 | this.m_previewedItem = evt.itemID;
13 | this.m_outfitSystem.EquipPuppetItem(this.GetGamePuppet(), this.m_previewedItem);
14 | }
15 | } else {
16 | wrappedMethod(evt);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/scripts/Overrides/EquipmentSystem.reds:
--------------------------------------------------------------------------------
1 | import EquipmentEx.OutfitSystem
2 |
3 | @addField(EquipmentSystemPlayerData)
4 | private let m_visualChangesAllowed: Bool;
5 |
6 | @addField(EquipmentSystemPlayerData)
7 | private let m_outfitSystem: wref;
8 |
9 | @wrapMethod(EquipmentSystemPlayerData)
10 | public final func OnAttach() {
11 | wrappedMethod();
12 |
13 | this.m_outfitSystem = OutfitSystem.GetInstance(this.m_owner.GetGame());
14 | }
15 |
16 | @addMethod(EquipmentSystemPlayerData)
17 | public func LockVisualChanges() {
18 | this.m_visualChangesAllowed = false;
19 | }
20 |
21 | @addMethod(EquipmentSystemPlayerData)
22 | public func UnlockVisualChanges() {
23 | this.m_visualChangesAllowed = true;
24 | }
25 |
26 | @wrapMethod(EquipmentSystemPlayerData)
27 | public final const func IsVisualSetActive() -> Bool {
28 | return wrappedMethod() || this.m_outfitSystem.IsActive();
29 | }
30 |
31 | @wrapMethod(EquipmentSystemPlayerData)
32 | public final const func IsSlotOverriden(area: gamedataEquipmentArea) -> Bool {
33 | return wrappedMethod(area) || (this.m_outfitSystem.IsManagedArea(area) && this.m_outfitSystem.IsActive());
34 | }
35 |
36 | @wrapMethod(EquipmentSystemPlayerData)
37 | private final const func ShouldUnderwearBeVisibleInSet() -> Bool {
38 | return !this.m_outfitSystem.IsActive() && !this.m_visualChangesAllowed && wrappedMethod();
39 | }
40 |
41 | @wrapMethod(EquipmentSystemPlayerData)
42 | private final const func ShouldUnderwearTopBeVisibleInSet() -> Bool {
43 | return !this.m_outfitSystem.IsActive() && !this.m_visualChangesAllowed && wrappedMethod();
44 | }
45 |
46 | @wrapMethod(EquipmentSystemPlayerData)
47 | public final func OnRestored() {
48 | this.m_outfitSystem = OutfitSystem.GetInstance(this.m_owner.GetGame());
49 | this.m_wardrobeSystem = GameInstance.GetWardrobeSystem(this.m_owner.GetGame());
50 |
51 | if NotEquals(this.m_wardrobeSystem.GetActiveClothingSetIndex(), gameWardrobeClothingSetIndex.INVALID) {
52 | this.m_wardrobeSystem.SetActiveClothingSetIndex(gameWardrobeClothingSetIndex.INVALID);
53 | this.m_lastActiveWardrobeSet = gameWardrobeClothingSetIndex.INVALID;
54 | }
55 |
56 | if !this.m_outfitSystem.IsActive() {
57 | let i = 0;
58 | while i <= ArraySize(this.m_clothingVisualsInfo) {
59 | this.m_clothingVisualsInfo[i].isHidden = false;
60 | this.m_clothingVisualsInfo[i].visualItem = ItemID.None();
61 | i += 1;
62 | }
63 | }
64 |
65 | wrappedMethod();
66 | }
67 |
68 | @replaceMethod(EquipmentSystemPlayerData)
69 | public final func OnQuestDisableWardrobeSetRequest(request: ref) {
70 | if this.m_outfitSystem.IsActive() {
71 | this.m_outfitSystem.Deactivate();
72 | this.m_lastActiveWardrobeSet = gameWardrobeClothingSetIndex.Slot1;
73 | }
74 |
75 | if request.blockReequipping {
76 | this.m_outfitSystem.Disable();
77 | }
78 | }
79 |
80 | @replaceMethod(EquipmentSystemPlayerData)
81 | public final func OnQuestRestoreWardrobeSetRequest(request: ref) {
82 | this.m_outfitSystem.Enable();
83 |
84 | if NotEquals(this.m_lastActiveWardrobeSet, gameWardrobeClothingSetIndex.INVALID) {
85 | this.m_outfitSystem.Reactivate();
86 | this.m_lastActiveWardrobeSet = gameWardrobeClothingSetIndex.INVALID;
87 | }
88 | }
89 |
90 | @replaceMethod(EquipmentSystemPlayerData)
91 | public final func OnQuestEnableWardrobeSetRequest(request: ref) {
92 | this.m_outfitSystem.Enable();
93 | }
94 |
95 | @replaceMethod(EquipmentSystemPlayerData)
96 | public final func EquipWardrobeSet(setID: gameWardrobeClothingSetIndex) {}
97 |
98 | @replaceMethod(EquipmentSystemPlayerData)
99 | public final func UnequipWardrobeSet() {}
100 |
101 | @replaceMethod(EquipmentSystemPlayerData)
102 | public final func QuestHideSlot(area: gamedataEquipmentArea) {}
103 |
104 | @replaceMethod(EquipmentSystemPlayerData)
105 | public final func QuestRestoreSlot(area: gamedataEquipmentArea) {}
106 |
107 | @wrapMethod(EquipmentSystemPlayerData)
108 | private final func ClearItemAppearanceEvent(area: gamedataEquipmentArea) {
109 | if this.m_visualChangesAllowed || !this.m_outfitSystem.IsActive() {
110 | wrappedMethod(area);
111 | }
112 | }
113 |
114 | @wrapMethod(EquipmentSystemPlayerData)
115 | private final func ResetItemAppearanceEvent(area: gamedataEquipmentArea) {
116 | if this.m_visualChangesAllowed || !this.m_outfitSystem.IsActive() {
117 | wrappedMethod(area);
118 | }
119 | }
120 |
121 | @wrapMethod(EquipmentSystemPlayerData)
122 | private final func ResetItemAppearance(area: gamedataEquipmentArea, opt force: Bool) {
123 | wrappedMethod(area, force);
124 |
125 | if Equals(area, gamedataEquipmentArea.Feet) && !this.IsSlotHidden(area) && !this.IsSlotOverriden(area) {
126 | let itemID = this.GetActiveItem(area);
127 | if ItemID.IsValid(itemID) {
128 | let slotID = this.GetPlacementSlotByAreaType(area);
129 | let transactionSystem = GameInstance.GetTransactionSystem(this.m_owner.GetGame());
130 | transactionSystem.RemoveItemFromSlot(this.m_owner, slotID);
131 | GameInstance.GetDelaySystem(this.m_owner.GetGame()).DelayCallback(EquipmentSystemReattachItem.Create(this, slotID, itemID), 1.0 / 60.0, false);
132 | }
133 | }
134 | }
135 |
136 | class EquipmentSystemReattachItem extends DelayCallback {
137 | protected let m_data: ref;
138 | protected let m_slotID: TweakDBID;
139 | protected let m_itemID: ItemID;
140 |
141 | public func Call() {
142 | let transactionSystem = GameInstance.GetTransactionSystem(this.m_data.m_owner.GetGame());
143 | transactionSystem.AddItemToSlot(this.m_data.m_owner, this.m_slotID, this.m_itemID, true);
144 | }
145 |
146 | public static func Create(data: ref, slotID: TweakDBID, itemID: ItemID) -> ref {
147 | let self = new EquipmentSystemReattachItem();
148 | self.m_data = data;
149 | self.m_slotID = slotID;
150 | self.m_itemID = itemID;
151 |
152 | return self;
153 | }
154 | }
155 |
--------------------------------------------------------------------------------
/scripts/Overrides/InventoryItemDisplayController.reds:
--------------------------------------------------------------------------------
1 | import EquipmentEx.OutfitSystem
2 |
3 | @addField(InventoryItemDisplayController)
4 | private let m_outfitSystem: wref;
5 |
6 | @wrapMethod(InventoryItemDisplayController)
7 | public func Bind(inventoryDataManager: ref, equipmentArea: gamedataEquipmentArea, opt slotIndex: Int32, opt displayContext: ItemDisplayContext, opt setWardrobeOutfit: Bool, opt wardrobeOutfitIndex: Int32) {
8 | this.m_outfitSystem = OutfitSystem.GetInstance(inventoryDataManager.GetGame());
9 |
10 | wrappedMethod(inventoryDataManager, equipmentArea, slotIndex, displayContext, setWardrobeOutfit, wardrobeOutfitIndex);
11 | }
12 |
13 | @wrapMethod(InventoryItemDisplayController)
14 | public func Bind(inventoryScriptableSystem: ref, equipmentArea: gamedataEquipmentArea, opt slotIndex: Int32, displayContext: ItemDisplayContext) {
15 | this.m_outfitSystem = OutfitSystem.GetInstance(inventoryScriptableSystem.GetGameInstance());
16 |
17 | wrappedMethod(inventoryScriptableSystem, equipmentArea, slotIndex, displayContext);
18 | }
19 |
20 | @wrapMethod(InventoryItemDisplayController)
21 | protected func RefreshUI() {
22 | let isOutfit = Equals(this.m_equipmentArea, gamedataEquipmentArea.Outfit);
23 | let isOverriden = this.m_outfitSystem.IsActive();
24 |
25 | if isOutfit && isOverriden {
26 | this.m_wardrobeOutfitIndex = 1;
27 | } else {
28 | this.m_wardrobeOutfitIndex = -1;
29 | }
30 |
31 | wrappedMethod();
32 |
33 | if isOutfit && isOverriden {
34 | inkWidgetRef.SetVisible(this.m_wardrobeInfoText, false);
35 | inkWidgetRef.SetVisible(this.m_slotItemsCountWrapper, false);
36 | inkWidgetRef.SetMargin(this.m_wardrobeInfoContainer, new inkMargin(12.0, 0, 0, 12.0));
37 | }
38 | }
39 |
40 | @wrapMethod(InventoryItemDisplayController)
41 | protected func NewUpdateRequirements(itemData: ref) {
42 | if !itemData.IsForWardrobe() {
43 | wrappedMethod(itemData);
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/scripts/Overrides/InventoryItemModeLogicController.reds:
--------------------------------------------------------------------------------
1 | import EquipmentEx.OutfitSystem
2 |
3 | @addField(InventoryItemModeLogicController)
4 | private let m_outfitSystem: wref;
5 |
6 | @addField(InventoryItemModeLogicController)
7 | public let m_isWardrobeScreen: Bool;
8 |
9 | @wrapMethod(InventoryItemModeLogicController)
10 | public final func SetupData(buttonHints: wref, tooltipsManager: wref, inventoryManager: ref, player: wref) {
11 | wrappedMethod(buttonHints, tooltipsManager, inventoryManager, player);
12 |
13 | this.m_outfitSystem = OutfitSystem.GetInstance(this.m_player.GetGame());
14 | }
15 |
16 | @replaceMethod(InventoryItemModeLogicController)
17 | private final func UpdateOutfitWardrobe(active: Bool, activeSetOverride: Int32) {
18 | inkWidgetRef.SetVisible(this.m_wardrobeSlotsContainer, active);
19 | inkWidgetRef.SetVisible(this.m_wardrobeSlotsLabel, active);
20 | inkWidgetRef.SetVisible(this.m_outfitsFilterInfoText, active);
21 | inkWidgetRef.SetVisible(this.m_filterButtonsGrid, !active);
22 |
23 | if active && !this.m_outfitWardrobeSpawned {
24 | let wardrobeContainer = inkWidgetRef.Get(this.m_wardrobeSlotsContainer) as inkCompoundWidget;
25 |
26 | let wardrobeInfo = new inkText();
27 | wardrobeInfo.SetLocalizedTextString("UI-Wardrobe-Tooltip-OutfitInfo");
28 | wardrobeInfo.SetFontFamily("base\\gameplay\\gui\\fonts\\raj\\raj.inkfontfamily");
29 | wardrobeInfo.SetStyle(r"base\\gameplay\\gui\\common\\main_colors.inkstyle");
30 | wardrobeInfo.BindProperty(n"tintColor", n"MainColors.Red");
31 | wardrobeInfo.BindProperty(n"fontWeight", n"MainColors.BodyFontWeight");
32 | wardrobeInfo.BindProperty(n"fontSize", n"MainColors.ReadableXSmall");
33 | wardrobeInfo.SetWrapping(true, 660.0);
34 | wardrobeInfo.Reparent(wardrobeContainer);
35 |
36 | // let wardrobeLink = this.SpawnFromLocal(wardrobeContainer, n"HyperlinkButton:EquipmentEx.WardrobeHubLink");
37 | // wardrobeLink.RegisterToCallback(n"OnClick", this.m_inventoryController, n"OnWardrobeScreenClick");
38 | // wardrobeLink.SetMargin(new inkMargin(16.0, 0.0, 0.0, 0.0));
39 |
40 | let wardrobeBtn = this.SpawnFromLocal(wardrobeContainer, n"wardrobeOutfitSlot:EquipmentEx.WardrobeHubBtnController");
41 | wardrobeBtn.SetMargin(new inkMargin(16.0, 0.0, 0.0, 0.0));
42 |
43 | this.m_outfitWardrobeSpawned = true;
44 | }
45 | }
46 |
47 | @replaceMethod(InventoryItemModeLogicController)
48 | protected cb func OnWardrobeOutfitSlotClicked(e: ref) -> Bool {
49 | this.m_inventoryController.ShowWardrobeScreen();
50 | }
51 |
52 | @replaceMethod(InventoryItemModeLogicController)
53 | protected cb func OnWardrobeOutfitSlotHoverOver(e: ref) -> Bool {
54 | //
55 | }
56 |
57 | @wrapMethod(InventoryItemModeLogicController)
58 | protected cb func OnItemDisplayClick(evt: ref) -> Bool {
59 | if !this.m_isWardrobeScreen {
60 | wrappedMethod(evt);
61 | }
62 | }
63 |
64 | @wrapMethod(InventoryItemModeLogicController)
65 | protected cb func OnItemDisplayHoverOver(evt: ref) -> Bool {
66 | if !this.m_isWardrobeScreen {
67 | wrappedMethod(evt);
68 | }
69 | }
70 |
71 | @wrapMethod(InventoryItemModeLogicController)
72 | private final func SetInventoryItemButtonHintsHoverOver(const displayingData: script_ref,
73 | opt display: ref) {
74 | wrappedMethod(displayingData, display);
75 |
76 | if this.m_outfitSystem.IsActive() {
77 | let equipmentArea = InventoryItemData.GetEquipmentArea(displayingData);
78 | let isClothing = this.IsEquipmentAreaClothing(equipmentArea) || Equals(equipmentArea, gamedataEquipmentArea.Outfit);
79 | if isClothing {
80 | this.m_buttonHintsController.RemoveButtonHint(n"preview_item");
81 | }
82 | }
83 | }
84 |
85 | @wrapMethod(InventoryItemModeLogicController)
86 | private final func HandleItemClick(const itemData: script_ref, actionName: ref, opt displayContext: ItemDisplayContext, opt isPlayerLocked: Bool) {
87 | if this.m_outfitSystem.IsActive() && actionName.IsAction(n"preview_item") {
88 | let equipmentArea = InventoryItemData.GetEquipmentArea(itemData);
89 | let isClothing = this.IsEquipmentAreaClothing(equipmentArea) || Equals(equipmentArea, gamedataEquipmentArea.Outfit);
90 | if isClothing {
91 | return;
92 | }
93 | }
94 |
95 | wrappedMethod(itemData, actionName, displayContext, isPlayerLocked);
96 | }
97 |
--------------------------------------------------------------------------------
/scripts/Overrides/PhotoModePlayerEntityComponent.reds:
--------------------------------------------------------------------------------
1 | import EquipmentEx.{OutfitSystem,PaperdollHelper}
2 |
3 | @addField(PhotoModePlayerEntityComponent)
4 | private let m_outfitSystem: wref;
5 |
6 | @addField(PhotoModePlayerEntityComponent)
7 | private let m_paperdollHelper: wref;
8 |
9 | @wrapMethod(PhotoModePlayerEntityComponent)
10 | private final func OnGameAttach() {
11 | wrappedMethod();
12 |
13 | this.m_outfitSystem = OutfitSystem.GetInstance(this.GetOwner().GetGame());
14 | this.m_paperdollHelper = PaperdollHelper.GetInstance(this.GetOwner().GetGame());
15 | }
16 |
17 | @wrapMethod(PhotoModePlayerEntityComponent)
18 | private final func SetupInventory(isCurrentPlayerObjectCustomizable: Bool) {
19 | wrappedMethod(isCurrentPlayerObjectCustomizable);
20 |
21 | if this.customizable {
22 | this.m_paperdollHelper.AddPuppet(this.fakePuppet);
23 |
24 | if this.m_outfitSystem.IsActive() {
25 | this.m_outfitSystem.EquipPuppetOutfit(this.fakePuppet, this.loadingItems);
26 | }
27 | }
28 | }
29 |
30 | @wrapMethod(PhotoModePlayerEntityComponent)
31 | protected cb func OnItemAddedToSlot(evt: ref) -> Bool {
32 | if this.m_outfitSystem.IsActive() {
33 | ArrayRemove(this.loadingItems, evt.GetItemID());
34 | } else {
35 | wrappedMethod(evt);
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/scripts/Overrides/PopupsManager.reds:
--------------------------------------------------------------------------------
1 | @wrapMethod(PopupsManager)
2 | private final func ShowTutorial() {
3 | if Equals(this.m_tutorialData.message, "LocKey#86091") || Equals(this.m_tutorialData.message, "LocKey#86092") {
4 | this.OnPopupCloseRequest(null);
5 | return;
6 | }
7 |
8 | wrappedMethod();
9 | }
10 |
--------------------------------------------------------------------------------
/scripts/Overrides/QuestTrackerGameController.reds:
--------------------------------------------------------------------------------
1 | import EquipmentEx.{CompatibilityManager, ConflictsPopup, RequirementsPopup}
2 |
3 | @addField(QuestTrackerGameController)
4 | private let m_wardrobePopup: ref;
5 |
6 | @wrapMethod(QuestTrackerGameController)
7 | protected cb func OnInitialize() -> Bool {
8 | wrappedMethod();
9 |
10 | if !CompatibilityManager.IsUserNotified() {
11 | if !CompatibilityManager.CheckRequirements() {
12 | this.m_wardrobePopup = RequirementsPopup.Show(this);
13 | this.m_wardrobePopup.RegisterListener(this, n"OnWardrobePopupClose");
14 | } else {
15 | if !CompatibilityManager.CheckConflicts(this.m_player.GetGame()) {
16 | this.m_wardrobePopup = ConflictsPopup.Show(this);
17 | this.m_wardrobePopup.RegisterListener(this, n"OnWardrobePopupClose");
18 | }
19 | }
20 | CompatibilityManager.MarkAsNotified();
21 | }
22 | }
23 |
24 | @addMethod(QuestTrackerGameController)
25 | protected cb func OnWardrobePopupClose(data: ref) {
26 | this.m_wardrobePopup = null;
27 | }
28 |
--------------------------------------------------------------------------------
/scripts/Overrides/Stash.reds:
--------------------------------------------------------------------------------
1 | import EquipmentEx.InventoryHelper
2 |
3 | @addMethod(Stash)
4 | protected cb func OnGameAttached() -> Bool {
5 | InventoryHelper.GetInstance(this.GetGame()).AddStash(this);
6 | }
7 |
--------------------------------------------------------------------------------
/scripts/Overrides/UIInventoryItem.reds:
--------------------------------------------------------------------------------
1 | @addMethod(UIInventoryItem)
2 | public static func Make(owner: wref, slotID: TweakDBID, itemData: script_ref, opt manager: wref) -> ref {
3 | let self = UIInventoryItem.FromInventoryItemData(owner, itemData, manager);
4 | self.m_data.IconPath = UIInventoryItemsManager.ResolveItemIconName(self.m_itemTweakID, self.m_itemRecord, self.m_manager);
5 | self.m_slotID = slotID;
6 |
7 | return self;
8 | }
9 |
10 | @addMethod(UIInventoryItem)
11 | public func IsForWardrobe() -> Bool {
12 | return TDBID.IsValid(this.m_slotID);
13 | }
14 |
15 | @wrapMethod(UIInventoryItem)
16 | public final func IsEquipped(opt force: Bool) -> Bool {
17 | if this.IsForWardrobe() && IsDefined(this.m_manager) {
18 | return this.m_manager.IsItemEquippedInSlot(this.ID, this.m_slotID);
19 | }
20 |
21 | return wrappedMethod(force);
22 | }
23 |
24 | @wrapMethod(UIInventoryItem)
25 | public final func IsTransmogItem() -> Bool {
26 | if this.IsForWardrobe() {
27 | return false;
28 | }
29 |
30 | return wrappedMethod();
31 | }
32 |
--------------------------------------------------------------------------------
/scripts/Overrides/UIInventoryItemsManager.reds:
--------------------------------------------------------------------------------
1 | import EquipmentEx.OutfitSystem
2 |
3 | @addField(UIInventoryItemsManager)
4 | private let m_outfitSystem: wref;
5 |
6 | @wrapMethod(UIInventoryItemsManager)
7 | public final static func Make(player: wref, transactionSystem: ref, uiScriptableSystem: wref) -> ref {
8 | let instance = wrappedMethod(player, transactionSystem, uiScriptableSystem);
9 | instance.m_outfitSystem = OutfitSystem.GetInstance(player.GetGame());
10 |
11 | return instance;
12 | }
13 |
14 | @addMethod(UIInventoryItemsManager)
15 | public final func IsItemEquippedInSlot(itemID: ItemID, slotID: TweakDBID) -> Bool {
16 | return this.m_outfitSystem.IsActive() ? this.m_outfitSystem.IsEquipped(itemID) : this.IsItemEquipped(itemID);
17 | }
18 |
19 | @wrapMethod(UIInventoryItemsManager)
20 | public final func IsItemTransmog(itemID: ItemID) -> Bool {
21 | return this.m_outfitSystem.IsActive() && this.m_outfitSystem.IsEquipped(itemID);
22 | }
23 |
--------------------------------------------------------------------------------
/scripts/Overrides/WardrobeSetPreviewGameController.reds:
--------------------------------------------------------------------------------
1 | import EquipmentEx.OutfitSystem
2 |
3 | @addField(WardrobeSetPreviewGameController)
4 | private let m_outfitSystem: wref;
5 |
6 | @wrapMethod(WardrobeSetPreviewGameController)
7 | protected cb func OnInitialize() -> Bool {
8 | wrappedMethod();
9 |
10 | let cameraSetup: gameuiPuppetPreviewCameraSetup;
11 | cameraSetup.slotName = n"UISlotPreview_UpperBody";
12 | cameraSetup.cameraZoom = 1.85;
13 | cameraSetup.interpolationTime = 1;
14 |
15 | ArrayResize(this.cameraController.cameraSetup, Cast(EnumGetMax(n"InventoryPaperdollZoomArea") + 1l));
16 | this.cameraController.cameraSetup[EnumInt(InventoryPaperdollZoomArea.Head)] = cameraSetup;
17 | }
18 |
19 | @wrapMethod(WardrobeSetPreviewGameController)
20 | protected cb func OnPreviewInitialized() -> Bool {
21 | this.m_outfitSystem = OutfitSystem.GetInstance(this.GetGamePuppet().GetGame());
22 |
23 | if this.m_isNotification && this.m_outfitSystem.IsActive() {
24 | this.m_outfitSystem.EquipPuppetOutfit(this.GetGamePuppet());
25 | this.m_outfitSystem.EquipPuppetItem(this.GetGamePuppet(), this.m_data.itemID);
26 | } else {
27 | wrappedMethod();
28 | }
29 | }
30 |
31 | @wrapMethod(WardrobeSetPreviewGameController)
32 | public final func RestorePuppetEquipment() {
33 | wrappedMethod();
34 |
35 | if this.m_outfitSystem.IsActive() {
36 | this.m_outfitSystem.EquipPuppetOutfit(this.GetGamePuppet());
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/scripts/Overrides/WardrobeUIGameController.reds:
--------------------------------------------------------------------------------
1 | import EquipmentEx.{CompatibilityManager, OutfitSystem, ArchivePopup, RequirementsPopup}
2 |
3 | @addField(WardrobeUIGameController)
4 | private let m_wardrobePopup: ref;
5 |
6 | @replaceMethod(WardrobeUIGameController)
7 | protected cb func OnInitialize() -> Bool {
8 | this.GetChildWidgetByPath(n"mainScreenContainer").SetVisible(false);
9 | this.GetChildWidgetByPath(n"setEditorScreenContainer").SetVisible(false);
10 | this.GetChildWidgetByPath(n"constantContainer/paperDoll").SetVisible(false);
11 |
12 | if !CompatibilityManager.CheckRequirements() {
13 | this.m_wardrobePopup = RequirementsPopup.Show(this);
14 | this.m_wardrobePopup.RegisterListener(this, n"OnWardrobePopupClose");
15 | } else {
16 | let wardrobe = this.SpawnFromExternal(this.GetRootCompoundWidget(), r"equipment_ex\\gui\\wardrobe.inkwidget", n"Root:EquipmentEx.WardrobeScreenController");
17 | if !IsDefined(wardrobe) {
18 | this.m_wardrobePopup = ArchivePopup.Show(this);
19 | this.m_wardrobePopup.RegisterListener(this, n"OnWardrobePopupClose");
20 | return false;
21 | }
22 | }
23 |
24 | this.m_introAnimProxy = new inkAnimProxy();
25 | }
26 |
27 | @replaceMethod(WardrobeUIGameController)
28 | protected cb func OnBack(userData: ref) -> Bool {
29 | this.m_menuEventDispatcher.SpawnEvent(n"OnWardrobeClose");
30 | }
31 |
32 | @replaceMethod(WardrobeUIGameController)
33 | private final func CloseWardrobe() -> Void {
34 | this.m_menuEventDispatcher.SpawnEvent(n"OnWardrobeClose");
35 | }
36 |
37 | @addMethod(WardrobeUIGameController)
38 | protected cb func OnWardrobePopupClose(data: ref) {
39 | this.m_wardrobePopup = null;
40 | this.m_menuEventDispatcher.SpawnEvent(n"OnWardrobeClose");
41 | }
42 |
--------------------------------------------------------------------------------
/scripts/Overrides/gameuiInGameMenuGameController.reds:
--------------------------------------------------------------------------------
1 | import EquipmentEx.OutfitSystem
2 |
3 | @addField(gameuiInGameMenuGameController)
4 | private let m_outfitSystem: wref;
5 |
6 | @wrapMethod(gameuiInGameMenuGameController)
7 | protected cb func OnInitialize() -> Bool {
8 | wrappedMethod();
9 |
10 | this.m_outfitSystem = OutfitSystem.GetInstance(this.GetPlayerControlledObject().GetGame());
11 | }
12 |
13 | @wrapMethod(gameuiInGameMenuGameController)
14 | protected cb func OnPuppetReady(sceneName: CName, puppet: ref) -> Bool {
15 | wrappedMethod(sceneName, puppet);
16 |
17 | if this.m_outfitSystem.IsActive() && Equals(sceneName, n"inventory") && !GetPlayer(puppet.GetGame()).IsReplacer() {
18 | this.m_outfitSystem.EquipPuppetOutfit(puppet) ;
19 | }
20 | }
21 |
22 | @wrapMethod(gameuiInGameMenuGameController)
23 | protected cb func OnEquipmentChanged(value: Variant) -> Bool {
24 | if !this.m_outfitSystem.UpdatePuppetFromBlackboard(this.GetPuppet(n"inventory")) {
25 | wrappedMethod(value);
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/scripts/Overrides/gameuiInventoryGameController.reds:
--------------------------------------------------------------------------------
1 | import EquipmentEx.{CompatibilityManager, OutfitSystem, ArchivePopup, RequirementsPopup}
2 |
3 | @addField(gameuiInventoryGameController)
4 | private let m_outfitSystem: wref;
5 |
6 | @addField(gameuiInventoryGameController)
7 | private let m_wardrobeButton: wref;
8 |
9 | @addField(gameuiInventoryGameController)
10 | private let m_wardrobePopup: ref;
11 |
12 | @addField(gameuiInventoryGameController)
13 | private let m_wardrobeReady: Bool;
14 |
15 | @wrapMethod(gameuiInventoryGameController)
16 | protected cb func OnInitialize() -> Bool {
17 | wrappedMethod();
18 |
19 | this.m_outfitSystem = OutfitSystem.GetInstance(this.GetPlayerControlledObject().GetGame());
20 | }
21 |
22 | @wrapMethod(gameuiInventoryGameController)
23 | protected cb func OnUninitialize() -> Bool {
24 | wrappedMethod();
25 |
26 | this.m_wardrobeButton.UnregisterFromCallback(n"OnClick", this, n"OnWardrobeBtnClick");
27 | }
28 |
29 | @replaceMethod(gameuiInventoryGameController)
30 | private final func SetupSetButton() -> Void {
31 | let btnWrapper = this.GetChildWidgetByPath(n"default_wrapper/menuLinks") as inkCompoundWidget;
32 | let btnList = this.GetChildWidgetByPath(n"default_wrapper/menuLinks/btnsContainer") as inkCompoundWidget;
33 |
34 | //
35 | btnList.GetWidgetByIndex(3).SetVisible(false);
36 | btnList.GetWidgetByIndex(4).SetVisible(false);
37 |
38 | // Spawn new button
39 | this.m_wardrobeButton = this.SpawnFromLocal(btnList, n"HyperlinkButton:EquipmentEx.WardrobeHubLinkController");
40 | this.m_wardrobeButton.RegisterToCallback(n"OnClick", this, n"OnWardrobeBtnClick");
41 |
42 | // Adjust button container size
43 | let btnSpacing = btnList.GetChildMargin();
44 | btnWrapper.SetHeight(btnWrapper.GetHeight() + this.m_wardrobeButton.GetHeight() + btnSpacing.top);
45 |
46 | // Adjust fluff text position
47 | let fluff = btnWrapper.GetWidget(n"buttonFluff2");
48 | fluff.SetAnchor(inkEAnchor.BottomLeft);
49 | fluff.SetMargin(new inkMargin(0, 0, 0, 4.0));
50 |
51 | // Force hide original button
52 | inkWidgetRef.SetVisible(this.m_btnSets, false);
53 | }
54 |
55 | @addMethod(gameuiInventoryGameController)
56 | protected cb func OnWardrobeBtnClick(evt: ref) -> Bool {
57 | if evt.IsAction(n"click") {
58 | this.ShowWardrobeScreen();
59 | }
60 | }
61 |
62 | @addMethod(gameuiInventoryGameController)
63 | protected cb func OnWardrobePopupClose(data: ref) {
64 | this.m_wardrobePopup = null;
65 | }
66 |
67 | @wrapMethod(gameuiInventoryGameController)
68 | protected cb func OnBack(userData: ref) -> Bool {
69 | if this.m_wardrobeReady && IsDefined(this.GetChildWidgetByPath(n"wardrobe")) {
70 | return this.HideWardrobeScreen();
71 | } else {
72 | return wrappedMethod(userData);
73 | }
74 | }
75 |
76 | @addMethod(gameuiInventoryGameController)
77 | protected func ShowWardrobeScreen() -> Bool {
78 | if !CompatibilityManager.CheckRequirements() {
79 | this.m_wardrobePopup = RequirementsPopup.Show(this);
80 | this.m_wardrobePopup.RegisterListener(this, n"OnWardrobePopupClose");
81 | return false;
82 | }
83 |
84 | if IsDefined(this.GetChildWidgetByPath(n"wardrobe")) {
85 | return false;
86 | }
87 |
88 | let wardrobe = this.SpawnFromExternal(this.GetRootCompoundWidget(), r"equipment_ex\\gui\\wardrobe.inkwidget", n"Root:EquipmentEx.WardrobeScreenController") as inkCompoundWidget;
89 |
90 | if !IsDefined(wardrobe) {
91 | this.m_wardrobePopup = ArchivePopup.Show(this);
92 | this.m_wardrobePopup.RegisterListener(this, n"OnWardrobePopupClose");
93 | return false;
94 | }
95 |
96 | wardrobe.SetName(n"wardrobe");
97 |
98 | let alphaAnim = new inkAnimTransparency();
99 | alphaAnim.SetStartTransparency(0.0);
100 | alphaAnim.SetEndTransparency(1.0);
101 | alphaAnim.SetType(inkanimInterpolationType.Linear);
102 | alphaAnim.SetMode(inkanimInterpolationMode.EasyOut);
103 | alphaAnim.SetDuration(0.8);
104 |
105 | let animDef = new inkAnimDef();
106 | animDef.AddInterpolator(alphaAnim);
107 |
108 | wardrobe.GetWidgetByPathName(n"wrapper/wrapper").PlayAnimation(animDef);
109 | // animProxy.RegisterToCallback(inkanimEventType.OnFinish, this, n"OnWardrobeScreenShown");
110 |
111 | this.m_wardrobeReady = true;
112 |
113 | if Equals(this.m_mode, InventoryModes.Item) {
114 | this.PlayShowHideItemChooserAnimation(false);
115 | } else {
116 | this.PlayLibraryAnimation(n"default_wrapper_outro");
117 | }
118 |
119 | this.GetChildWidgetByPath(n"wardrobe/wrapper/preview").SetVisible(false);
120 | this.PlaySlidePaperdollAnimationToOutfit();
121 |
122 | this.m_buttonHintsController.Hide();
123 |
124 | let evt = new DropQueueUpdatedEvent();
125 | evt.m_dropQueue = this.m_itemModeLogicController.m_itemDropQueue;
126 | wardrobe.GetController().QueueEvent(evt);
127 |
128 | this.m_itemModeLogicController.m_isWardrobeScreen = true;
129 |
130 | return true;
131 | }
132 |
133 | // @addMethod(gameuiInventoryGameController)
134 | // protected cb func OnWardrobeScreenShown(anim: ref) {
135 | // LogDebug(s"OnWardrobeScreenShown \(this.GetRootCompoundWidget().GetNumChildren())");
136 | // this.m_wardrobeReady = true;
137 | // }
138 |
139 | @addMethod(gameuiInventoryGameController)
140 | protected func HideWardrobeScreen() -> Bool {
141 | if !this.m_wardrobeReady {
142 | return false;
143 | }
144 |
145 | let wardrobe = this.GetChildWidgetByPath(n"wardrobe") as inkCompoundWidget;
146 |
147 | this.m_wardrobeReady = false;
148 |
149 | let alphaAnim = new inkAnimTransparency();
150 | alphaAnim.SetStartTransparency(1.0);
151 | alphaAnim.SetEndTransparency(0.0);
152 | alphaAnim.SetType(inkanimInterpolationType.Linear);
153 | alphaAnim.SetMode(inkanimInterpolationMode.EasyOut);
154 | alphaAnim.SetDuration(0.3);
155 |
156 | let animDef = new inkAnimDef();
157 | animDef.AddInterpolator(alphaAnim);
158 |
159 | let animProxy = wardrobe.GetWidgetByPathName(n"wrapper/wrapper").PlayAnimation(animDef);
160 | animProxy.RegisterToCallback(inkanimEventType.OnFinish, this, n"OnWardrobeScreenHidden");
161 |
162 | if Equals(this.m_mode, InventoryModes.Item) {
163 | this.SwapMode(InventoryModes.Default);
164 | this.m_itemModeLogicController.m_isShown = false;
165 | }
166 |
167 | this.PlayLibraryAnimation(n"default_wrapper_Intro");
168 |
169 | this.GetChildWidgetByPath(n"wardrobe/wrapper/preview").SetVisible(false);
170 | inkWidgetRef.SetVisible(this.m_paperDollWidget, true);
171 |
172 | this.PlaySlidePaperdollAnimation(PaperdollPositionAnimation.Center, false);
173 | this.ZoomCamera(EnumInt(InventoryPaperdollZoomArea.Default));
174 |
175 | this.m_buttonHintsController.Show();
176 |
177 | this.m_itemModeLogicController.m_isWardrobeScreen = false;
178 |
179 | return true;
180 | }
181 |
182 | @addMethod(gameuiInventoryGameController)
183 | protected cb func OnWardrobeScreenHidden(anim: ref) {
184 | this.GetRootCompoundWidget().RemoveChildByName(n"wardrobe");
185 | }
186 |
187 | @addMethod(gameuiInventoryGameController)
188 | protected final func PlaySlidePaperdollAnimationToOutfit() {
189 | let outfitPreview = this.GetChildWidgetByPath(n"wardrobe/wrapper/preview");
190 | let outfitPreviewMargin = outfitPreview.GetMargin();
191 |
192 | let translationInterpolator = new inkAnimTranslation();
193 | translationInterpolator.SetDuration(0.2);
194 | translationInterpolator.SetDirection(inkanimInterpolationDirection.FromTo);
195 | translationInterpolator.SetType(inkanimInterpolationType.Linear);
196 | translationInterpolator.SetMode(inkanimInterpolationMode.EasyIn);
197 | translationInterpolator.SetStartTranslation(inkWidgetRef.GetTranslation(this.m_paperDollWidget));
198 | translationInterpolator.SetEndTranslation(new Vector2(outfitPreviewMargin.left, 0.00));
199 |
200 | let translationAnimation = new inkAnimDef();
201 | translationAnimation.AddInterpolator(translationInterpolator);
202 |
203 | let animProxy = inkWidgetRef.PlayAnimation(this.m_paperDollWidget, translationAnimation);
204 | animProxy.RegisterToCallback(inkanimEventType.OnFinish, this, n"OnPaperDollSlideComplete");
205 | }
206 |
207 | @addMethod(gameuiInventoryGameController)
208 | protected cb func OnPaperDollSlideComplete(anim: ref) {
209 | inkWidgetRef.SetVisible(this.m_paperDollWidget, false);
210 | this.GetChildWidgetByPath(n"wardrobe/wrapper/preview").SetVisible(true);
211 | }
212 |
213 | @wrapMethod(gameuiInventoryGameController)
214 | protected cb func OnEquipmentClick(evt: ref) -> Bool {
215 | if IsDefined(this.GetChildWidgetByPath(n"wardrobe")) {
216 | return false;
217 | }
218 |
219 | if evt.actionName.IsAction(n"unequip_item") && Equals(evt.display.GetEquipmentArea(), gamedataEquipmentArea.Outfit) && this.m_outfitSystem.IsActive() {
220 | this.m_outfitSystem.Deactivate();
221 | } else {
222 | wrappedMethod(evt);
223 | }
224 | }
225 |
226 | @replaceMethod(gameuiInventoryGameController)
227 | private final func RefreshEquippedWardrobeItems() {
228 | ArrayClear(this.m_wardrobeOutfitAreas);
229 |
230 | if this.m_outfitSystem.IsActive() {
231 | ArrayPush(this.m_wardrobeOutfitAreas, gamedataEquipmentArea.Head);
232 | ArrayPush(this.m_wardrobeOutfitAreas, gamedataEquipmentArea.Face);
233 | ArrayPush(this.m_wardrobeOutfitAreas, gamedataEquipmentArea.OuterChest);
234 | ArrayPush(this.m_wardrobeOutfitAreas, gamedataEquipmentArea.InnerChest);
235 | ArrayPush(this.m_wardrobeOutfitAreas, gamedataEquipmentArea.Legs);
236 | ArrayPush(this.m_wardrobeOutfitAreas, gamedataEquipmentArea.Feet);
237 | }
238 | }
239 |
--------------------------------------------------------------------------------
/scripts/Overrides/gameuiPhotoModeMenuController.reds:
--------------------------------------------------------------------------------
1 | import EquipmentEx.{OutfitSystem,PaperdollHelper}
2 |
3 | enum PhotoModeUI {
4 | CharacterPage = 2,
5 | VisibilityAttribute = 27,
6 | ExpressionAttribute = 28,
7 | OutfitAttribute = 3301,
8 | NoOutfitOption = 3302,
9 | CurrentOutfitOption = 3303
10 | }
11 |
12 | @addField(gameuiPhotoModeMenuController)
13 | private let m_outfitSystem: wref;
14 |
15 | @addField(gameuiPhotoModeMenuController)
16 | private let m_paperdollHelper: wref;
17 |
18 | @addField(gameuiPhotoModeMenuController)
19 | private let m_outfitAttribute: Uint32;
20 |
21 | @wrapMethod(gameuiPhotoModeMenuController)
22 | protected cb func OnInitialize() -> Bool {
23 | wrappedMethod();
24 |
25 | this.m_outfitSystem = OutfitSystem.GetInstance(this.GetPlayerControlledObject().GetGame());
26 | this.m_paperdollHelper = PaperdollHelper.GetInstance(this.GetPlayerControlledObject().GetGame());
27 | this.m_outfitAttribute = Cast(EnumInt(PhotoModeUI.OutfitAttribute));
28 | }
29 |
30 | @wrapMethod(gameuiPhotoModeMenuController)
31 | protected cb func OnAddMenuItem(label: String, attribute: Uint32, page: Uint32) -> Bool {
32 | wrappedMethod(label, attribute, page);
33 |
34 | if Equals(page, Cast(EnumInt(PhotoModeUI.CharacterPage))) && Equals(attribute, Cast(EnumInt(PhotoModeUI.VisibilityAttribute))) {
35 | this.AddMenuItem(StrUpper(GetLocalizedTextByKey(n"UI-Inventory-Labels-Outfit")), this.m_outfitAttribute, page, false);
36 | }
37 | }
38 |
39 | @wrapMethod(gameuiPhotoModeMenuController)
40 | protected cb func OnShow(reversedUI: Bool) -> Bool {
41 | let outfitMenuItem = this.GetMenuItem(this.m_outfitAttribute);
42 | if IsDefined(outfitMenuItem) {
43 | let outfits = this.m_outfitSystem.GetOutfits();
44 | let active = this.m_outfitSystem.IsActive();
45 | let options: array;
46 | let current: Int32 = 0;
47 |
48 | ArrayResize(options, ArraySize(outfits) + (active ? 2 : 1));
49 |
50 | options[0].optionText = GetLocalizedTextByKey(n"UI-Wardrobe-NoOutfit");
51 | options[0].optionData = EnumInt(PhotoModeUI.NoOutfitOption);
52 |
53 | if active {
54 | options[1].optionText = GetLocalizedTextByKey(n"UI-Wardrobe-CurrentOutfit");
55 | options[1].optionData = EnumInt(PhotoModeUI.CurrentOutfitOption);
56 | }
57 |
58 | let i = (active ? 2 : 1);
59 | for outfitName in outfits {
60 | options[i].optionText = NameToString(outfitName); // StrUpper()
61 | options[i].optionData = i;
62 |
63 | if this.m_outfitSystem.IsEquipped(outfitName) {
64 | current = options[i].optionData;
65 | }
66 |
67 | i += 1;
68 | }
69 |
70 | if current == 0 {
71 | current = options[active ? 1 : 0].optionData;
72 | }
73 |
74 | outfitMenuItem.m_photoModeController = this;
75 | outfitMenuItem.SetupOptionSelector(options, current);
76 | outfitMenuItem.SetIsEnabled(true);
77 |
78 | this.GetChildWidgetByPath(n"options_panel").SetHeight(1000.0);
79 | this.GetChildWidgetByPath(n"options_panel/horizontalMenu").SetMargin(0.0, 0.0, -10.0, 920.0);
80 | }
81 |
82 | wrappedMethod(reversedUI);
83 | }
84 |
85 | @wrapMethod(gameuiPhotoModeMenuController)
86 | protected cb func OnSetAttributeOptionEnabled(attribute: Uint32, enabled: Bool) -> Bool {
87 | wrappedMethod(attribute, enabled);
88 |
89 | if Equals(attribute, Cast(EnumInt(PhotoModeUI.ExpressionAttribute))) {
90 | let outfitMenuItem = this.GetMenuItem(this.m_outfitAttribute);
91 | if IsDefined(outfitMenuItem) {
92 | outfitMenuItem.SetIsEnabled(enabled);
93 | }
94 | }
95 | }
96 |
97 | @addMethod(gameuiPhotoModeMenuController)
98 | public func OnAttributeOptionSelected(attribute: Uint32, option: PhotoModeOptionSelectorData) {
99 | if Equals(attribute, Cast(EnumInt(PhotoModeUI.OutfitAttribute))) {
100 | switch IntEnum(option.optionData) {
101 | case PhotoModeUI.NoOutfitOption:
102 | this.m_outfitSystem.EquipPuppetOutfit(this.m_paperdollHelper.GetPuppet(), false);
103 | break;
104 | case PhotoModeUI.CurrentOutfitOption:
105 | this.m_outfitSystem.EquipPuppetOutfit(this.m_paperdollHelper.GetPuppet(), true);
106 | break;
107 | default:
108 | let outfitName = StringToName(option.optionText);
109 | this.m_outfitSystem.EquipPuppetOutfit(this.m_paperdollHelper.GetPuppet(), outfitName);
110 | break;
111 | }
112 | }
113 | }
114 |
115 | @wrapMethod(PhotoModeMenuListItem)
116 | private final func StartArrowClickedEffect(widget: inkWidgetRef) {
117 | wrappedMethod(widget);
118 |
119 | this.m_photoModeController.OnAttributeOptionSelected(
120 | (this.GetData() as PhotoModeMenuListItemData).attributeKey,
121 | this.m_OptionSelectorValues[this.m_OptionSelector.GetCurrIndex()]
122 | );
123 | }
124 |
--------------------------------------------------------------------------------
/scripts/Overrides/inkInventoryPuppetPreviewGameController.reds:
--------------------------------------------------------------------------------
1 | import EquipmentEx.PaperdollHelper
2 |
3 | @wrapMethod(inkInventoryPuppetPreviewGameController)
4 | protected cb func OnInitialize() -> Bool {
5 | wrappedMethod();
6 |
7 | PaperdollHelper.GetInstance(this.GetPlayerControlledObject().GetGame()).AddPreview(this);
8 | }
9 |
--------------------------------------------------------------------------------
/scripts/Overrides/inkScrollController.reds:
--------------------------------------------------------------------------------
1 | @addMethod(inkScrollController)
2 | public func SetScrollEnabled(enabled: Bool) {
3 | if enabled {
4 | if Equals(this.direction, inkEScrollDirection.Horizontal) {
5 | this.scrollDelta = this.contentSize.X - this.viewportSize.X;
6 | } else {
7 | this.scrollDelta = this.contentSize.Y - this.viewportSize.Y;
8 | }
9 | } else {
10 | this.scrollDelta = 0.0;
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/scripts/PaperdollHelper.reds:
--------------------------------------------------------------------------------
1 | module EquipmentEx
2 |
3 | public class PaperdollHelper extends ScriptableSystem {
4 | private let m_puppet: wref;
5 | private let m_preview: wref;
6 |
7 | public func AddPreview(preview: ref) {
8 | this.m_preview = preview;
9 | }
10 |
11 | public func GetPreview() -> wref {
12 | return this.m_preview;
13 | }
14 |
15 | public func AddPuppet(puppet: ref) {
16 | this.m_puppet = puppet;
17 | }
18 |
19 | public func GetPuppet() -> wref {
20 | return this.m_puppet;
21 | }
22 |
23 | public static func GetInstance(game: GameInstance) -> ref {
24 | return GameInstance.GetScriptableSystemsContainer(game).Get(n"EquipmentEx.PaperdollHelper") as PaperdollHelper;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/scripts/Tweaks/OutfitSlotMatcher.reds:
--------------------------------------------------------------------------------
1 | module EquipmentEx
2 |
3 | struct RecordSlotMapping {
4 | public let slotID: TweakDBID;
5 | public let recordIDs: array;
6 | }
7 |
8 | struct EntityNameSlotMapping {
9 | public let slotID: TweakDBID;
10 | public let entityName: CName;
11 | }
12 |
13 | struct AppearanceNameSlotMapping {
14 | public let slotID: TweakDBID;
15 | public let appearanceTokens: array;
16 | }
17 |
18 | struct EquipmentAreaSlotMapping {
19 | public let slotID: TweakDBID;
20 | public let equipmentAreas: array;
21 | }
22 |
23 | struct PriceModifierSlotMapping {
24 | public let slotID: TweakDBID;
25 | public let priceModifiers: array;
26 | }
27 |
28 | struct SlotMappingMatch {
29 | public let slotID: TweakDBID;
30 | public let score: Int32;
31 | }
32 |
33 | class OutfitSlotMatcher {
34 | private let m_recordMappings: array;
35 | private let m_entityMappings: array;
36 | private let m_appearanceMappings: array;
37 | private let m_equipmentMappings: array;
38 | private let m_priceMappings: array;
39 | private let m_ignoredEntities: array;
40 |
41 | public func MapRecords(mappings: array) {
42 | this.m_recordMappings = mappings;
43 | }
44 |
45 | public func MapEntities(mappings: array) {
46 | this.m_entityMappings = mappings;
47 | }
48 |
49 | public func MapAppearances(mappings: array) {
50 | this.m_appearanceMappings = mappings;
51 | }
52 |
53 | public func MapEquipmentAreas(mappings: array) {
54 | this.m_equipmentMappings = mappings;
55 | }
56 |
57 | public func MapPrices(mappings: array) {
58 | this.m_priceMappings = mappings;
59 | }
60 |
61 | public func IgnoreEntities(ignores: array) {
62 | this.m_ignoredEntities = ignores;
63 | }
64 |
65 | public func Match(item: ref) -> TweakDBID {
66 | if Equals(item.AppearanceName(), n"") {
67 | return TDBID.None();
68 | }
69 |
70 | let entityName = item.EntityName();
71 |
72 | if ArrayContains(this.m_ignoredEntities, entityName) {
73 | return TDBID.None();
74 | }
75 |
76 | let recordID = item.GetID();
77 | let appearanceName = NameToString(item.AppearanceName());
78 | let priceModifiers = TweakDBInterface.GetForeignKeyArray(item.GetID() + t".buyPrice");
79 | let equipmentArea = item.EquipArea().GetID();
80 |
81 | // Record exact match
82 | for mapping in this.m_recordMappings {
83 | if ArrayContains(mapping.recordIDs, recordID) {
84 | return mapping.slotID;
85 | }
86 | }
87 |
88 | // Appearance exact match
89 | for mapping in this.m_appearanceMappings {
90 | for appearanceToken in mapping.appearanceTokens {
91 | if Equals(appearanceName, appearanceToken) {
92 | return mapping.slotID;
93 | }
94 | }
95 | }
96 |
97 | // Appearance partial match
98 | let match: SlotMappingMatch;
99 | for mapping in this.m_appearanceMappings {
100 | for appearanceToken in mapping.appearanceTokens {
101 | if StrFindFirst(appearanceName, appearanceToken) >= 0 {
102 | // return mapping.slotID;
103 | if StrLen(appearanceToken) > match.score {
104 | match.score = StrLen(appearanceToken);
105 | match.slotID = mapping.slotID;
106 | }
107 | }
108 | }
109 | }
110 | if match.score > 0 {
111 | return match.slotID;
112 | }
113 |
114 | // Price exact match
115 | for mapping in this.m_priceMappings {
116 | for priceModifier in mapping.priceModifiers {
117 | if ArrayContains(priceModifiers, priceModifier) {
118 | return mapping.slotID;
119 | }
120 | }
121 | }
122 |
123 | // Equipment area exact match
124 | for mapping in this.m_equipmentMappings {
125 | if ArrayContains(mapping.equipmentAreas, equipmentArea) {
126 | return mapping.slotID;
127 | }
128 | }
129 |
130 | // Entity exact match
131 | for mapping in this.m_entityMappings {
132 | if Equals(entityName, mapping.entityName) {
133 | return mapping.slotID;
134 | }
135 | }
136 |
137 | return TDBID.None();
138 | }
139 |
140 | public static func Create() -> ref {
141 | return new OutfitSlotMatcher();
142 | }
143 | }
144 |
--------------------------------------------------------------------------------
/scripts/Tweaks/OutfitTweakHelper.reds:
--------------------------------------------------------------------------------
1 | module EquipmentEx
2 |
3 | class OutfitTweakHelper {
4 | public static func PrepareCustomSlotMatcher() -> ref {
5 | let slotMatcher = OutfitSlotMatcher.Create();
6 |
7 | slotMatcher.IgnoreEntities([
8 | n"player_head_item",
9 | n"player_face_item",
10 | n"player_inner_torso_item",
11 | n"player_outer_torso_item",
12 | n"player_legs_item",
13 | n"player_feet_item",
14 | n"player_outfit_item"
15 | ]);
16 |
17 | slotMatcher.MapPrices([
18 | new PriceModifierSlotMapping(t"OutfitSlots.Glasses", [t"Price.Glasses", t"Price.Visor"]),
19 | new PriceModifierSlotMapping(t"OutfitSlots.Wreath", [t"Price.TechFaceClothing"]),
20 | new PriceModifierSlotMapping(t"OutfitSlots.LegsOuter", [t"Price.Skirt"])
21 | ]);
22 |
23 | slotMatcher.MapEquipmentAreas([
24 | new EquipmentAreaSlotMapping(t"OutfitSlots.Head", [t"EquipmentArea.HeadArmor"]),
25 | new EquipmentAreaSlotMapping(t"OutfitSlots.Mask", [t"EquipmentArea.FaceArmor"]),
26 | new EquipmentAreaSlotMapping(t"OutfitSlots.TorsoInner", [t"EquipmentArea.InnerChest"]),
27 | new EquipmentAreaSlotMapping(t"OutfitSlots.TorsoOuter", [t"EquipmentArea.ChestArmor"]),
28 | new EquipmentAreaSlotMapping(t"OutfitSlots.LegsMiddle", [t"EquipmentArea.LegArmor"]),
29 | new EquipmentAreaSlotMapping(t"OutfitSlots.Feet", [t"EquipmentArea.Feet"]),
30 | new EquipmentAreaSlotMapping(t"OutfitSlots.BodyOuter", [t"EquipmentArea.Outfit"])
31 | ]);
32 |
33 | return slotMatcher;
34 | }
35 |
36 | public static func PrepareOriginalSlotMatcher() -> ref {
37 | let slotMatcher = OutfitSlotMatcher.Create();
38 |
39 | //slotMatcher.IgnoreEntities([
40 | // n"player_outfit_item"
41 | //]);
42 |
43 | slotMatcher.MapEntities([
44 | new EntityNameSlotMapping(t"OutfitSlots.Head", n"player_head_item"),
45 | new EntityNameSlotMapping(t"OutfitSlots.Mask", n"player_face_item"),
46 | new EntityNameSlotMapping(t"OutfitSlots.TorsoInner", n"player_inner_torso_item"),
47 | new EntityNameSlotMapping(t"OutfitSlots.TorsoOuter", n"player_outer_torso_item"),
48 | new EntityNameSlotMapping(t"OutfitSlots.LegsMiddle", n"player_legs_item"),
49 | new EntityNameSlotMapping(t"OutfitSlots.Feet", n"player_feet_item"),
50 | new EntityNameSlotMapping(t"OutfitSlots.BodyMiddle", n"player_outfit_item"),
51 | new EntityNameSlotMapping(t"OutfitSlots.BodyMiddle", n"player_outfit_item_ep1")
52 | ]);
53 |
54 | slotMatcher.MapAppearances([
55 | new AppearanceNameSlotMapping(t"OutfitSlots.Glasses", ["f1_tech_01_"]),
56 | new AppearanceNameSlotMapping(t"OutfitSlots.Wreath", ["f1_tech_02_"]),
57 | new AppearanceNameSlotMapping(t"OutfitSlots.Balaclava", ["h1_balaclava_"]),
58 | new AppearanceNameSlotMapping(t"OutfitSlots.TorsoUnder", ["t1_undershirt_02_", "t1_undershirt_03_", "t1_shirt_01_", "t1_shirt_02_"]),
59 | new AppearanceNameSlotMapping(t"OutfitSlots.TorsoInner", ["t1_undershirt_01_", "t1_tshirt_", "t1_formal_", "set_01_fixer_01_t1_"]),
60 | new AppearanceNameSlotMapping(t"OutfitSlots.TorsoInner", ["t2_dress_01_", "t2_jacket_16_"]),
61 | new AppearanceNameSlotMapping(t"OutfitSlots.TorsoMiddle", ["t1_shirt_03_"]),
62 | new AppearanceNameSlotMapping(t"OutfitSlots.TorsoMiddle", ["t2_dress_", "t2_shirt_", "t2_vest_", "t2_formal_"]),
63 | new AppearanceNameSlotMapping(t"OutfitSlots.TorsoAux", ["t2_vest_01_", "t2_vest_02_", "t2_vest_03_", "t2_vest_04_", "t2_vest_06_", "t2_vest_07_", "t2_vest_08_", "t2_vest_10_", "t2_vest_12_", "t2_vest_16_"]),
64 | new AppearanceNameSlotMapping(t"OutfitSlots.LegsOuter", ["l1_shorts_03_", "l1_shorts_04_", "l1_shorts_05_", "set_01_fixer_01_l1_"]),
65 | new AppearanceNameSlotMapping(t"OutfitSlots.LegsOuter", ["l1_pants_04_", "l1_pants_05_", "l1_pants_06_", "l1_pants_07_", "l1_pants_08_", "l1_pants_09_", "l1_pants_10_", "l1_pants_11_", "l1_pants_12_", "l1_pants_13_", "l1_pants_14_"]),
66 | new AppearanceNameSlotMapping(t"OutfitSlots.BodyUnder", ["t1_jumpsuit_", "set_01_netrunner_01_t1_"]),
67 | new AppearanceNameSlotMapping(t"OutfitSlots.BodyMiddle", ["t2_jumpsuit_"]) // "outfit_02_q114_cyberspace_"
68 | ]);
69 |
70 | slotMatcher.MapPrices([
71 | new PriceModifierSlotMapping(t"OutfitSlots.Mask", [t"Price.Mask"]),
72 | new PriceModifierSlotMapping(t"OutfitSlots.Glasses", [t"Price.Glasses", t"Price.Visor"]),
73 | new PriceModifierSlotMapping(t"OutfitSlots.Wreath", [t"Price.TechFaceClothing"]),
74 | new PriceModifierSlotMapping(t"OutfitSlots.LegsOuter", [t"Price.Skirt"])
75 | ]);
76 |
77 | slotMatcher.MapRecords([
78 | new RecordSlotMapping(t"OutfitSlots.Glasses", [
79 | t"Items.Media_01_Set_Tech",
80 | t"Items.Techie_01_Set_Tech"
81 | ]),
82 | new RecordSlotMapping(t"OutfitSlots.TorsoUnder", [
83 | t"Items.Media_01_Set_Shirt"
84 | ]),
85 | new RecordSlotMapping(t"OutfitSlots.TorsoMiddle", [
86 | t"Items.Corporate_01_Set_FormalJacket",
87 | t"Items.Rockerboy_01_Set_Jacket"
88 | ]),
89 | new RecordSlotMapping(t"OutfitSlots.TorsoAux", [
90 | t"Items.Media_01_Set_Vest",
91 | t"Items.SQ021_Wraiths_Vest",
92 | t"Items.Techie_01_Set_Vest"
93 | ]),
94 | new RecordSlotMapping(t"OutfitSlots.LegsOuter", [
95 | t"Items.Cop_01_Set_Pants",
96 | t"Items.Media_01_Set_Pants",
97 | t"Items.Netrunner_01_Set_Pants",
98 | t"Items.Nomad_01_Set_Pants",
99 | t"Items.Q202_Epilogue_Pants",
100 | t"Items.Q203_Epilogue_Pants",
101 | t"Items.Q204_Epilogue_Pants",
102 | t"Items.Solo_01_Set_Pants",
103 | t"Items.Techie_01_Set_Pants"
104 | ])
105 | ]);
106 |
107 | return slotMatcher;
108 | }
109 |
110 | public static func BuildOutfitSlotMap(out outfitSlots: array) -> ref {
111 | let outfitMap = new inkIntHashMap();
112 | let index = 0;
113 | for outfitSlot in outfitSlots {
114 | outfitMap.Insert(TDBID.ToNumber(outfitSlot.slotID), index);
115 | index = index + 1;
116 | }
117 | return outfitMap;
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/scripts/Tweaks/PatchCustomItems.reds:
--------------------------------------------------------------------------------
1 | module EquipmentEx
2 |
3 | class PatchCustomItems extends ScriptableTweak {
4 | protected func OnApply() -> Void {
5 | let batch = TweakDBManager.StartBatch();
6 | let outfitSlots = OutfitConfig.OutfitSlots();
7 | let outfitMap = OutfitTweakHelper.BuildOutfitSlotMap(outfitSlots);
8 | let slotMatcher = OutfitTweakHelper.PrepareCustomSlotMatcher();
9 |
10 | for record in TweakDBInterface.GetRecords(n"Clothing_Record") {
11 | let item = record as Clothing_Record;
12 | let placementSlots = TweakDBInterface.GetForeignKeyArray(item.GetID() + t".placementSlots");
13 |
14 | if ArraySize(placementSlots) == 1 {
15 | let outfitSlotID = slotMatcher.Match(item);
16 | if TDBID.IsValid(outfitSlotID) {
17 | let outfitHash = TDBID.ToNumber(outfitSlotID);
18 | if outfitMap.KeyExist(outfitHash) {
19 | let outfitIndex = outfitMap.Get(outfitHash);
20 | let outfitSlot = outfitSlots[outfitIndex];
21 | if !ArrayContains(placementSlots, outfitSlot.slotID) {
22 | ArrayPush(placementSlots, outfitSlot.slotID);
23 | batch.SetFlat(item.GetID() + t".placementSlots", placementSlots);
24 | batch.UpdateRecord(item.GetID());
25 | }
26 | }
27 | }
28 | }
29 | }
30 |
31 | batch.Commit();
32 |
33 | for record in TweakDBInterface.GetRecords(n"Clothing_Record") {
34 | let item = record as Clothing_Record;
35 | let placementSlots = TweakDBInterface.GetForeignKeyArray(item.GetID() + t".placementSlots");
36 | let garmentOffset = item.GarmentOffset();
37 |
38 | if (garmentOffset == 0 || DevMode()) && ArraySize(placementSlots) > 1 {
39 | let outfitSlotID = ArrayLast(placementSlots);
40 | if TDBID.IsValid(outfitSlotID) {
41 | let outfitHash = TDBID.ToNumber(outfitSlotID);
42 | if outfitMap.KeyExist(outfitHash) {
43 | let outfitIndex = outfitMap.Get(outfitHash);
44 | let outfitSlot = outfitSlots[outfitIndex];
45 | batch.SetFlat(item.GetID() + t".garmentOffset", outfitSlot.garmentOffset);
46 | batch.UpdateRecord(item.GetID());
47 | }
48 | }
49 | }
50 | }
51 |
52 | batch.Commit();
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/scripts/Tweaks/PatchOriginaltems.reds:
--------------------------------------------------------------------------------
1 | module EquipmentEx
2 |
3 | class PatchOriginaltems extends ScriptableTweak {
4 | protected func OnApply() -> Void {
5 | let batch = TweakDBManager.StartBatch();
6 | let outfitSlots = OutfitConfig.OutfitSlots();
7 | let outfitMap = OutfitTweakHelper.BuildOutfitSlotMap(outfitSlots);
8 | let slotMatcher = OutfitTweakHelper.PrepareOriginalSlotMatcher();
9 |
10 | for record in TweakDBInterface.GetRecords(n"Clothing_Record") {
11 | let item = record as Clothing_Record;
12 | let placementSlots = TweakDBInterface.GetForeignKeyArray(item.GetID() + t".placementSlots");
13 | let garmentOffset = item.GarmentOffset();
14 |
15 | let outfitSlotID: TweakDBID;
16 | if ArraySize(placementSlots) == 1 || DevMode() {
17 | outfitSlotID = slotMatcher.Match(item);
18 | } else {
19 | outfitSlotID = ArrayLast(placementSlots);
20 | }
21 |
22 | if TDBID.IsValid(outfitSlotID) {
23 | let updated = false;
24 |
25 | let outfitHash = TDBID.ToNumber(outfitSlotID);
26 | if outfitMap.KeyExist(outfitHash) {
27 | let outfitIndex = outfitMap.Get(outfitHash);
28 | let outfitSlot = outfitSlots[outfitIndex];
29 |
30 | if NotEquals(ArrayLast(placementSlots), outfitSlot.slotID) {
31 | ArrayRemove(placementSlots, outfitSlot.slotID);
32 | ArrayPush(placementSlots, outfitSlot.slotID);
33 | if garmentOffset == 0 || DevMode() {
34 | garmentOffset = outfitSlot.garmentOffset;
35 | }
36 | updated = true;
37 | }
38 | }
39 |
40 | if updated {
41 | batch.SetFlat(item.GetID() + t".placementSlots", placementSlots);
42 | batch.SetFlat(item.GetID() + t".garmentOffset", garmentOffset);
43 | batch.UpdateRecord(item.GetID());
44 | }
45 | }
46 | }
47 |
48 | batch.Commit();
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/scripts/Tweaks/RegisterOutfitSlots.reds:
--------------------------------------------------------------------------------
1 | module EquipmentEx
2 |
3 | class RegisterOutfitSlots extends ScriptableTweak {
4 | protected func OnApply() -> Void {
5 | let batch = TweakDBManager.StartBatch();
6 | let outfitSlots = OutfitConfig.OutfitSlots();
7 |
8 | for outfitSlot in outfitSlots {
9 | batch.CreateRecord(outfitSlot.slotID, n"AttachmentSlot_Record");
10 | batch.SetFlat(outfitSlot.slotID + t".localizedName", outfitSlot.displayName);
11 |
12 | if ArraySize(outfitSlot.relatedSlotIDs) > 0 {
13 | batch.SetFlat(outfitSlot.slotID + t".parentSlot", outfitSlot.relatedSlotIDs[0]);
14 | }
15 |
16 | if ArraySize(outfitSlot.dependencySlotIDs) > 0 {
17 | batch.SetFlat(outfitSlot.slotID + t".dependencySlots", outfitSlot.dependencySlotIDs);
18 | }
19 |
20 | batch.UpdateRecord(outfitSlot.slotID);
21 | batch.RegisterName(outfitSlot.slotName);
22 | }
23 |
24 | let playerEntityTemplates = [
25 | r"base\\characters\\entities\\player\\player_wa_fpp.ent",
26 | r"base\\characters\\entities\\player\\player_wa_tpp.ent",
27 | r"base\\characters\\entities\\player\\player_wa_tpp_cutscene.ent",
28 | r"base\\characters\\entities\\player\\player_wa_tpp_cutscene_no_impostor.ent",
29 | r"base\\characters\\entities\\player\\player_wa_tpp_reflexion.ent",
30 | r"base\\characters\\entities\\player\\player_ma_fpp.ent",
31 | r"base\\characters\\entities\\player\\player_ma_tpp.ent",
32 | r"base\\characters\\entities\\player\\player_ma_tpp_cutscene.ent",
33 | r"base\\characters\\entities\\player\\player_ma_tpp_cutscene_no_impostor.ent",
34 | r"base\\characters\\entities\\player\\player_ma_tpp_reflexion.ent"
35 | ];
36 |
37 | let playerDisplayName = GetLocalizedTextByKey(TweakDBInterface.GetLocKeyDefault(t"Character.Player_Puppet_Base.displayName"));
38 |
39 | for record in TweakDBInterface.GetRecords(n"Character_Record") {
40 | let character = record as Character_Record;
41 | if ArrayContains(playerEntityTemplates, character.EntityTemplatePath()) || Equals(GetLocalizedTextByKey(character.DisplayName()), playerDisplayName) {
42 | let characterSlots = TweakDBInterface.GetForeignKeyArray(character.GetID() + t".attachmentSlots");
43 | if ArrayContains(characterSlots, t"AttachmentSlots.Chest") {
44 | for outfitSlot in outfitSlots {
45 | if !ArrayContains(characterSlots, outfitSlot.slotID) {
46 | ArrayPush(characterSlots, outfitSlot.slotID);
47 | }
48 | }
49 |
50 | batch.SetFlat(character.GetID() + t".attachmentSlots", characterSlots);
51 | batch.UpdateRecord(character.GetID());
52 | }
53 | }
54 | }
55 |
56 | batch.Commit();
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/scripts/UI/ArchivePopup.reds:
--------------------------------------------------------------------------------
1 | module EquipmentEx
2 |
3 | public class ArchivePopup {
4 | public static func Show(controller: ref) -> ref {
5 | return GenericMessageNotification.Show(
6 | controller,
7 | GetLocalizedText("LocKey#11447"),
8 | "Equipment-EX has detected an issue:\n" +
9 | "- archive/pc/mod/EquipmentEx.archive is missing\n\n" +
10 | "Possible solutions:\n" +
11 | "- Reinstall the mod from the original distribution\n" +
12 | "- If you installed it as REDmod, make sure mods are enabled\n",
13 | GenericMessageNotificationType.OK
14 | );
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/scripts/UI/CollapseButton.reds:
--------------------------------------------------------------------------------
1 | module EquipmentEx
2 | import Codeware.UI.*
3 |
4 | class CollapseButtonClick extends Event {
5 | public let collapse: Bool;
6 | public let action: ref;
7 | }
8 |
9 | class CollapseButton extends inkCustomController {
10 | protected let m_isFlipped: Bool;
11 | protected let m_isCollapse: Bool;
12 |
13 | protected let m_bg: wref;
14 | protected let m_frame: wref;
15 | protected let m_icon: wref;
16 |
17 | protected cb func OnCreate() {
18 | let root = new inkCanvas();
19 | root.SetSize(110.0, 80.0);
20 | root.SetAnchorPoint(new Vector2(0.5, 0.5));
21 | root.SetInteractive(true);
22 |
23 | let bg = new inkImage();
24 | bg.SetName(n"bg");
25 | bg.SetAnchor(inkEAnchor.Fill);
26 | bg.SetNineSliceScale(true);
27 | bg.SetAtlasResource(r"base\\gameplay\\gui\\common\\shapes\\atlas_shapes_sync.inkatlas");
28 | bg.SetStyle(r"base\\gameplay\\gui\\common\\components\\toggles_style.inkstyle");
29 | bg.BindProperty(n"tintColor", n"FilterButton.backgroundColor");
30 | bg.BindProperty(n"opacity", n"FilterButton.backgroundOpacity");
31 | bg.Reparent(root);
32 |
33 | let frame = new inkImage();
34 | frame.SetName(n"frame");
35 | frame.SetAnchor(inkEAnchor.Fill);
36 | frame.SetNineSliceScale(true);
37 | frame.SetAtlasResource(r"base\\gameplay\\gui\\common\\shapes\\atlas_shapes_sync.inkatlas");
38 | frame.SetTexturePart(n"tooltip_map_fg");
39 | frame.SetStyle(r"base\\gameplay\\gui\\common\\components\\toggles_style.inkstyle");
40 | frame.BindProperty(n"tintColor", n"FilterButton.frameColor");
41 | frame.BindProperty(n"opacity", n"FilterButton.frameOpacity");
42 | frame.Reparent(root);
43 |
44 | let icon = new inkVerticalPanel();
45 | icon.SetAnchor(inkEAnchor.Centered);
46 | icon.SetAnchorPoint(new Vector2(0.5, 0.5));
47 | icon.Reparent(root);
48 |
49 | let arrowScale = 0.4;
50 | let arrowSize = new Vector2(44.0 * arrowScale, 38.0 * arrowScale);
51 |
52 | let arrowUp = new inkImage();
53 | arrowUp.SetName(n"arrowUp");
54 | arrowUp.SetHAlign(inkEHorizontalAlign.Center);
55 | arrowUp.SetAtlasResource(r"base\\gameplay\\gui\\common\\shapes\\atlas_shapes_sync.inkatlas");
56 | arrowUp.SetTexturePart(n"arrow_rect_bg");
57 | arrowUp.SetSize(arrowSize);
58 | arrowUp.SetStyle(r"base\\gameplay\\gui\\common\\components\\toggles_style.inkstyle");
59 | arrowUp.BindProperty(n"tintColor", n"FilterButton.iconColor");
60 | arrowUp.Reparent(icon);
61 |
62 | let line = new inkRectangle();
63 | line.SetHAlign(inkEHorizontalAlign.Center);
64 | line.SetSize(new Vector2(arrowSize.X + 12.0, 2.0));
65 | line.SetStyle(r"base\\gameplay\\gui\\common\\components\\toggles_style.inkstyle");
66 | line.BindProperty(n"tintColor", n"FilterButton.iconColor");
67 | line.Reparent(icon);
68 |
69 | let arrowDown = new inkImage();
70 | arrowDown.SetName(n"arrowDown");
71 | arrowDown.SetHAlign(inkEHorizontalAlign.Center);
72 | arrowDown.SetAtlasResource(r"base\\gameplay\\gui\\common\\shapes\\atlas_shapes_sync.inkatlas");
73 | arrowDown.SetTexturePart(n"arrow_down_bg");
74 | arrowDown.SetSize(arrowSize);
75 | arrowDown.SetStyle(r"base\\gameplay\\gui\\common\\components\\toggles_style.inkstyle");
76 | arrowDown.BindProperty(n"tintColor", n"FilterButton.iconColor");
77 | arrowDown.Reparent(icon);
78 |
79 | this.m_bg = bg;
80 | this.m_frame = frame;
81 | this.m_icon = icon;
82 |
83 | this.SetRootWidget(root);
84 | this.ApplyCollapseState();
85 | this.ApplyFlippedState();
86 | }
87 |
88 | protected cb func OnInitialize() {
89 | this.RegisterToCallback(n"OnClick", this, n"OnClick");
90 | this.RegisterToCallback(n"OnHoverOver", this, n"OnHoverOver");
91 | this.RegisterToCallback(n"OnHoverOut", this, n"OnHoverOut");
92 | }
93 |
94 | protected cb func OnClick(evt: ref) {
95 | this.TriggerClickEvent(evt.GetActionName());
96 | }
97 |
98 | protected cb func OnHoverOver(evt: ref) {
99 | this.GetRootWidget().SetState(n"Hover");
100 | }
101 |
102 | protected cb func OnHoverOut(evt: ref) {
103 | this.GetRootWidget().SetState(n"Default");
104 | }
105 |
106 | protected func ApplyCollapseState() {
107 | this.m_icon.SetChildOrder(this.m_isCollapse ? inkEChildOrder.Backward : inkEChildOrder.Forward);
108 | this.m_icon.SetChildMargin(this.m_isCollapse ? new inkMargin(0.0, 3.0, 0.0, 3.0) : new inkMargin(0.0, 3.0, 0.0, 3.0));
109 | }
110 |
111 | protected func ApplyFlippedState() {
112 | this.m_bg.SetTexturePart(this.m_isFlipped ? n"cell_flip_bg" : n"cell_bg");
113 | this.m_frame.SetBrushMirrorType(this.m_isFlipped ? inkBrushMirrorType.Horizontal : inkBrushMirrorType.NoMirror);
114 | }
115 |
116 | protected func TriggerClickEvent(action: ref) {
117 | let evt = new CollapseButtonClick();
118 | evt.collapse = this.m_isCollapse;
119 | evt.action = action;
120 |
121 | let uiSystem = GameInstance.GetUISystem(this.GetGame());
122 | uiSystem.QueueEvent(evt);
123 | }
124 |
125 | public func SetCollapse(isCollapse: Bool) {
126 | this.m_isCollapse = isCollapse;
127 |
128 | this.ApplyCollapseState();
129 | }
130 |
131 | public func SetFlipped(isFlipped: Bool) {
132 | this.m_isFlipped = isFlipped;
133 |
134 | this.ApplyFlippedState();
135 | }
136 |
137 | public static func Create() -> ref {
138 | let self = new CollapseButton();
139 | self.CreateInstance();
140 |
141 | return self;
142 | }
143 |
144 | func OnReparent(parent: ref) {}
145 | }
146 |
--------------------------------------------------------------------------------
/scripts/UI/ConflictsPopup.reds:
--------------------------------------------------------------------------------
1 | module EquipmentEx
2 |
3 | public class ConflictsPopup {
4 | public static func Show(controller: ref) -> ref {
5 | let game = controller.GetPlayerControlledObject().GetGame();
6 | let conflicts: array;
7 | CompatibilityManager.CheckConflicts(game, conflicts);
8 |
9 | let conflictStr: String;
10 | for conflict in conflicts {
11 | conflictStr += "- " + conflict + "\n";
12 | }
13 |
14 | let params = new inkTextParams();
15 | params.AddString("conflicts", conflictStr);
16 |
17 | return GenericMessageNotification.Show(
18 | controller,
19 | GetLocalizedText("LocKey#11447"),
20 | GetLocalizedTextByKey(n"UI-EquipmentEx-NotificationConflicts"),
21 | params,
22 | GenericMessageNotificationType.OK
23 | );
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/scripts/UI/InventoryGridData.reds:
--------------------------------------------------------------------------------
1 | module EquipmentEx
2 |
3 | class InventoryGridItemData extends VendorUIInventoryItemData {
4 | public let Parent: wref;
5 | public let IsVisible: Bool;
6 | }
7 |
8 | class InventoryGridSlotData extends VendorUIInventoryItemData {
9 | public let Children: array][>;
10 | public let TotalItems: Int32;
11 | public let VisibleItems: Int32;
12 | public let IsCollapsed: Bool;
13 |
14 | protected func GetActiveItem() -> wref {
15 | for uiItem in this.Children {
16 | if uiItem.Item.IsEquipped() {
17 | return uiItem;
18 | }
19 | }
20 |
21 | return null;
22 | }
23 | }
24 |
25 | class InventoryGridDataView extends BackpackDataView {
26 | private let m_filter: Bool;
27 | private let m_refresh: Bool;
28 | private let m_reverse: Bool;
29 | private let m_searchQuery: String;
30 | private let m_viewManager: wref;
31 |
32 | public func SetViewManager(viewManager: wref) {
33 | this.m_viewManager = viewManager;
34 | }
35 |
36 | public func SetCollapsed(state: Bool) {
37 | this.m_viewManager.SetCollapsed(state);
38 | }
39 |
40 | public func ToggleCollapsed() {
41 | this.m_viewManager.ToggleCollapsed();
42 | }
43 |
44 | public func ToggleCollapsed(slotID: TweakDBID) {
45 | this.m_viewManager.ToggleCollapsed(slotID);
46 | }
47 |
48 | public func SetSearchQuery(searchQuery: String) {
49 | this.m_searchQuery = UTF8StrLower(searchQuery);
50 | }
51 |
52 | public func UpdateView() {
53 | this.DisableSorting();
54 | this.m_filter = true;
55 | this.Filter();
56 | this.m_filter = false;
57 | this.Filter();
58 | }
59 |
60 | public func FilterItem(data: ref) -> Bool {
61 | let uiItem = data as InventoryGridItemData;
62 |
63 | if IsDefined(uiItem) {
64 | if this.m_filter {
65 | uiItem.IsVisible = true;
66 |
67 | if Equals(this.m_itemFilterType, ItemFilterCategory.Clothes) {
68 | if !uiItem.Item.IsEquipped() {
69 | uiItem.IsVisible = false;
70 | }
71 | }
72 |
73 | if NotEquals(this.m_searchQuery, "") {
74 | let itemName = UTF8StrLower(GetLocalizedText(uiItem.Item.m_data.Name));
75 | if !StrContains(itemName, this.m_searchQuery) {
76 | uiItem.IsVisible = false;
77 | }
78 | }
79 | }
80 |
81 | return uiItem.IsVisible && !uiItem.Parent.IsCollapsed;
82 | }
83 |
84 | let uiSlot = data as InventoryGridSlotData;
85 |
86 | if IsDefined(uiSlot) {
87 | if this.m_filter {
88 | uiSlot.IsCollapsed = this.m_viewManager.IsCollapsed(uiSlot.ItemData.SlotID);
89 | } else {
90 | uiSlot.TotalItems = ArraySize(uiSlot.Children);
91 | uiSlot.VisibleItems = 0;
92 |
93 | for uiChildData in uiSlot.Children {
94 | if uiChildData.IsVisible {
95 | uiSlot.VisibleItems += 1;
96 | }
97 | }
98 | }
99 |
100 | return uiSlot.VisibleItems > 0;
101 | }
102 |
103 | return false;
104 | }
105 | }
106 |
107 | class InventoryGridTemplateClassifier extends inkVirtualItemTemplateClassifier {
108 | public func ClassifyItem(data: Variant) -> Uint32 {
109 | return IsDefined(FromVariant][>(data) as InventoryGridSlotData) ? 1u : 0u;
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/scripts/UI/InventoryGridItem.reds:
--------------------------------------------------------------------------------
1 | module EquipmentEx
2 |
3 | class InventoryGridItemController extends VendorItemVirtualController {
4 | protected cb func OnOutfitUpdated(evt: ref) {
5 | this.UpdateEquippedState();
6 | }
7 |
8 | protected cb func OnOutfitPartUpdated(evt: ref) {
9 | this.UpdateEquippedState();
10 | }
11 |
12 | protected func UpdateEquippedState() {
13 | this.m_itemViewController.NewUpdateEquipped(this.m_itemViewController.m_uiInventoryItem);
14 | this.m_itemViewController.NewUpdateLocked(this.m_itemViewController.m_uiInventoryItem);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/scripts/UI/InventoryGridSlot.reds:
--------------------------------------------------------------------------------
1 | module EquipmentEx
2 |
3 | class InventoryGridSlotClick extends Event {
4 | public let slot: ref;
5 | public let action: ref;
6 | }
7 |
8 | class InventoryGridSlotHoverOver extends Event {
9 | public let slot: ref;
10 | }
11 |
12 | class InventoryGridSlotHoverOut extends Event {
13 | public let slot: ref;
14 | }
15 |
16 | class InventoryGridSlotController extends inkVirtualCompoundItemController {
17 | private let m_uiSlot: ref;
18 |
19 | private let m_root: wref;
20 | private let m_arrow: wref;
21 | private let m_slotName: wref;
22 | private let m_itemName: wref;
23 | private let m_itemCount: wref;
24 |
25 | private let m_isToggled: Bool;
26 | private let m_isHovered: Bool;
27 |
28 | protected cb func OnInitialize() {
29 | this.m_root = this.GetRootCompoundWidget();
30 |
31 | let content = new inkVerticalPanel();
32 | content.SetName(n"content");
33 | content.SetHAlign(inkEHorizontalAlign.Left);
34 | content.SetVAlign(inkEVerticalAlign.Center);
35 | content.SetAnchor(inkEAnchor.CenterLeft);
36 | content.SetAnchorPoint(0.0, 0.5);
37 | content.SetMargin(new inkMargin(28.0, 0.0, 0.0, 4.0));
38 | content.Reparent(this.m_root);
39 |
40 | let slotName = new inkText();
41 | slotName.SetName(n"slot_name");
42 | slotName.SetFontFamily("base\\gameplay\\gui\\fonts\\raj\\raj.inkfontfamily");
43 | slotName.SetLetterCase(textLetterCase.UpperCase);
44 | slotName.SetStyle(r"base\\gameplay\\gui\\common\\main_colors.inkstyle");
45 | slotName.BindProperty(n"tintColor", n"MainColors.Red");
46 | slotName.BindProperty(n"fontWeight", n"MainColors.BodyFontWeight");
47 | slotName.BindProperty(n"fontSize", n"MainColors.ReadableFontSize");
48 | slotName.SetFitToContent(true);
49 | slotName.Reparent(content);
50 |
51 | let itemName = new inkText();
52 | itemName.SetName(n"item_name");
53 | itemName.SetFontFamily("base\\gameplay\\gui\\fonts\\raj\\raj.inkfontfamily");
54 | itemName.SetLetterCase(textLetterCase.UpperCase);
55 | itemName.SetStyle(r"base\\gameplay\\gui\\common\\main_colors.inkstyle");
56 | itemName.BindProperty(n"tintColor", n"MainColors.Blue");
57 | itemName.BindProperty(n"fontSize", n"MainColors.ReadableXSmall");
58 | itemName.SetFitToContent(true);
59 | itemName.Reparent(content);
60 |
61 | let itemCount = new inkText();
62 | itemCount.SetName(n"item_count");
63 | itemCount.SetFontFamily("base\\gameplay\\gui\\fonts\\raj\\raj.inkfontfamily");
64 | itemCount.SetLetterCase(textLetterCase.UpperCase);
65 | itemCount.SetStyle(r"base\\gameplay\\gui\\common\\main_colors.inkstyle");
66 | itemCount.BindProperty(n"tintColor", n"MainColors.Grey");
67 | itemCount.BindProperty(n"fontSize", n"MainColors.ReadableXSmall");
68 | itemCount.SetFitToContent(true);
69 | itemCount.Reparent(content);
70 |
71 | let panel = new inkCanvas();
72 | panel.SetName(n"panel");
73 | panel.SetAnchor(inkEAnchor.Fill);
74 | panel.SetMargin(new inkMargin(0.0, 2.0, 0.0, 8.0));
75 | panel.Reparent(this.m_root);
76 |
77 | let bg1 = new inkImage();
78 | bg1.SetName(n"bg1");
79 | bg1.SetAnchor(inkEAnchor.Fill);
80 | bg1.SetAnchorPoint(new Vector2(0.5, 0.5));
81 | bg1.SetNineSliceScale(true);
82 | bg1.SetAtlasResource(r"base\\gameplay\\gui\\common\\shapes\\atlas_shapes_sync.inkatlas");
83 | bg1.SetTexturePart(n"item_bg");
84 | bg1.SetStyle(r"base\\gameplay\\gui\\common\\components\\slots_style.inkstyle");
85 | bg1.BindProperty(n"tintColor", n"ItemDisplay.background");
86 | bg1.BindProperty(n"opacity", n"ItemDisplay.backgroundOpacity");
87 | bg1.Reparent(panel);
88 |
89 | let bg2 = new inkImage();
90 | bg2.SetName(n"bg2");
91 | bg2.SetAnchor(inkEAnchor.Fill);
92 | bg2.SetAnchorPoint(new Vector2(0.5, 0.5));
93 | bg2.SetNineSliceScale(true);
94 | bg2.SetNineSliceGrid(new inkMargin(0.0, 0.0, 20.0, 0.0));
95 | bg2.SetAtlasResource(r"base\\gameplay\\gui\\fullscreen\\inventory\\atlas_inventory.inkatlas");
96 | bg2.SetTexturePart(n"texture_2slot_iconic");
97 | bg2.SetOpacity(0.03);
98 | bg2.SetStyle(r"base\\gameplay\\gui\\common\\components\\slots_style.inkstyle");
99 | bg2.BindProperty(n"tintColor", n"ItemDisplay.emptyLinesColor");
100 | //bg2.BindProperty(n"opacity", n"ItemDisplay.emptyLinesOpacity");
101 | bg2.Reparent(panel);
102 |
103 | let fg = new inkImage();
104 | fg.SetName(n"fg");
105 | fg.SetAnchor(inkEAnchor.Fill);
106 | fg.SetAnchorPoint(new Vector2(0.5, 0.5));
107 | fg.SetNineSliceScale(true);
108 | fg.SetAtlasResource(r"base\\gameplay\\gui\\common\\shapes\\atlas_shapes_sync.inkatlas");
109 | fg.SetTexturePart(n"item_fg");
110 | fg.SetStyle(r"base\\gameplay\\gui\\common\\components\\slots_style.inkstyle");
111 | fg.BindProperty(n"tintColor", n"ItemDisplay.borderColor");
112 | fg.BindProperty(n"opacity", n"ItemDisplay.borderOpacity");
113 | fg.Reparent(panel);
114 |
115 | let arrow = new inkImage();
116 | arrow.SetName(n"arrow");
117 | arrow.SetAnchor(inkEAnchor.CenterRight);
118 | arrow.SetAnchorPoint(new Vector2(1.0, 0.5));
119 | arrow.SetMargin(new inkMargin(0.0, 0.0, 40.0, 0.0));
120 | arrow.SetFitToContent(true);
121 | arrow.SetAtlasResource(r"base\\gameplay\\gui\\common\\shapes\\atlas_shapes_sync.inkatlas");
122 | arrow.SetTexturePart(n"arrow_right_bg");
123 | arrow.SetStyle(r"base\\gameplay\\gui\\common\\components\\slots_style.inkstyle");
124 | arrow.BindProperty(n"tintColor", n"ItemDisplay.borderColor");
125 | arrow.BindProperty(n"opacity", n"ItemDisplay.borderOpacity");
126 | arrow.Reparent(panel);
127 |
128 | this.m_arrow = arrow;
129 | this.m_slotName = slotName;
130 | this.m_itemName = itemName;
131 | this.m_itemCount = itemCount;
132 |
133 | this.RegisterToCallback(n"OnClick", this, n"OnClick");
134 | this.RegisterToCallback(n"OnEnter", this, n"OnHoverOver");
135 | this.RegisterToCallback(n"OnLeave", this, n"OnHoverOut");
136 | }
137 |
138 | protected cb func OnDataChanged(value: Variant) {
139 | this.m_uiSlot = FromVariant][>(value) as InventoryGridSlotData;
140 |
141 | this.UpdateSlotInfo();
142 | this.UpdateActiveItem();
143 | this.UpdateState();
144 | }
145 |
146 | protected cb func OnOutfitUpdated(evt: ref) {
147 | this.UpdateActiveItem();
148 | }
149 |
150 | protected cb func OnOutfitPartUpdated(evt: ref) {
151 | if Equals(this.m_uiSlot.ItemData.SlotID, evt.slotID) {
152 | this.UpdateActiveItem();
153 | }
154 | }
155 |
156 | protected cb func OnClick(evt: ref) {
157 | this.TriggerClickEvent(evt.GetActionName());
158 | }
159 |
160 | protected cb func OnHoverOver(evt: ref) {
161 | this.UpdateState();
162 | this.TriggerHoverOverEvent();
163 | }
164 |
165 | protected cb func OnHoverOut(evt: ref) {
166 | this.UpdateState();
167 | this.TriggerHoverOutEvent();
168 | }
169 |
170 | protected func UpdateSlotInfo() {
171 | this.m_slotName.SetText(this.m_uiSlot.ItemData.CategoryName);
172 |
173 | let itemCount = GetLocalizedText("LocKey#53719") + ": ";
174 | if this.m_uiSlot.TotalItems != this.m_uiSlot.VisibleItems {
175 | itemCount += ToString(this.m_uiSlot.VisibleItems) + " / ";
176 | }
177 | itemCount += ToString(this.m_uiSlot.TotalItems);
178 |
179 | this.m_itemCount.SetText(itemCount);
180 | }
181 |
182 | protected func UpdateActiveItem() {
183 | let uiItem = this.m_uiSlot.GetActiveItem();
184 |
185 | if IsDefined(uiItem) {
186 | this.m_itemName.SetText(uiItem.Item.GetName());
187 | this.m_itemName.BindProperty(n"tintColor", n"MainColors.Blue");
188 | } else {
189 | this.m_itemName.SetText(GetLocalizedTextByKey(n"UI-Labels-EmptySlot"));
190 | this.m_itemName.BindProperty(n"tintColor", n"MainColors.Grey");
191 | }
192 | }
193 |
194 | protected func UpdateState() {
195 | this.m_arrow.SetRotation(this.m_uiSlot.IsCollapsed ? 0.0 : 90.0);
196 | }
197 |
198 | protected func TriggerClickEvent(action: ref) {
199 | let evt = new InventoryGridSlotClick();
200 | evt.slot = this.m_uiSlot;
201 | evt.action = action;
202 |
203 | this.QueueEvent(evt);
204 | }
205 |
206 | protected func TriggerHoverOverEvent() {
207 | let evt = new InventoryGridSlotHoverOver();
208 | evt.slot = this.m_uiSlot;
209 |
210 | this.QueueEvent(evt);
211 | }
212 |
213 | protected func TriggerHoverOutEvent() {
214 | let evt = new InventoryGridSlotHoverOut();
215 | evt.slot = this.m_uiSlot;
216 |
217 | this.QueueEvent(evt);
218 | }
219 | }
220 |
--------------------------------------------------------------------------------
/scripts/UI/ItemSourceOption.reds:
--------------------------------------------------------------------------------
1 | module EquipmentEx
2 |
3 | class ItemSourceOptionChange extends Event {
4 | public let value: WardrobeItemSource;
5 | }
6 |
7 | class ItemSourceOptionController extends inkButtonController {
8 | private let m_value: WardrobeItemSource;
9 |
10 | private let m_root: wref;
11 | private let m_label: wref;
12 | private let m_checkbox: wref;
13 | private let m_selection: wref;
14 |
15 | private let m_disabled: Bool;
16 | private let m_hovered: Bool;
17 | private let m_selected: Bool;
18 |
19 | protected cb func OnInitialize() {
20 | this.m_root = this.GetRootCompoundWidget();
21 |
22 | this.m_label = this.GetChildWidgetByPath(n"titleAndCheckbox/FilterName") as inkText;
23 | this.m_label.SetStyle(r"base\\gameplay\\gui\\common\\main_colors.inkstyle");
24 |
25 | this.m_checkbox = this.GetChildWidgetByPath(n"titleAndCheckbox/checkbox");
26 | this.m_selection = this.GetChildWidgetByPath(n"titleAndCheckbox/checkbox/checkbox");
27 |
28 | this.RegisterToCallback(n"OnRelease", this, n"OnRelease");
29 | this.RegisterToCallback(n"OnEnter", this, n"OnHoverOver");
30 | this.RegisterToCallback(n"OnLeave", this, n"OnHoverOut");
31 | }
32 |
33 | protected cb func OnRelease(evt: ref) {
34 | if evt.IsAction(n"click") && !this.m_disabled && !this.m_selected {
35 | this.TriggerChangeEvent();
36 | }
37 | }
38 |
39 | protected cb func OnHoverOver(evt: ref) {
40 | if !this.m_disabled {
41 | this.m_hovered = true;
42 |
43 | this.UpdateState();
44 | // this.TriggerHoverOverEvent();
45 | }
46 | }
47 |
48 | protected cb func OnHoverOut(evt: ref) {
49 | if !this.m_disabled {
50 | this.m_hovered = false;
51 |
52 | this.UpdateState();
53 | // this.TriggerHoverOutEvent();
54 | }
55 | }
56 |
57 | protected cb func OnOptionChange(evt: ref) {
58 | this.m_selected = Equals(this.m_value, evt.value);
59 | this.UpdateState();
60 | }
61 |
62 | protected func UpdateView() {
63 | this.m_root.SetSize(new Vector2(this.m_label.GetDesiredWidth() + 170.0, 80.0));
64 |
65 | this.m_label.SetText(GetLocalizedText("UI-EquipmentEx-WardrobeItemSource-" + ToString(this.m_value)));
66 | this.m_label.BindProperty(n"fontStyle", n"MainColors.BodyFontWeight");
67 | this.m_label.BindProperty(n"fontSize", n"MainColors.ReadableSmall");
68 |
69 | this.m_checkbox.SetVisible(true);
70 | this.m_label.SetMargin(new inkMargin(20, 0, 0, 0));
71 |
72 | this.GetChildWidgetByPath(n"titleAndCheckbox").SetMargin(new inkMargin(0, 0, 0, 0));
73 | this.GetChildWidgetByPath(n"background").SetVisible(false);
74 | }
75 |
76 | protected func UpdateState() {
77 | this.m_selection.SetVisible(this.m_selected);
78 |
79 | if this.m_disabled {
80 | this.m_root.SetState(n"Default");
81 | this.m_root.SetOpacity(0.3);
82 | } else {
83 | this.m_root.SetOpacity(1.0);
84 |
85 | if this.m_hovered {
86 | this.m_root.SetState(n"Hover");
87 | }
88 | else {
89 | if this.m_selected {
90 | this.m_root.SetState(n"Selected");
91 | }
92 | else {
93 | this.m_root.SetState(n"Default");
94 | }
95 | }
96 |
97 | this.m_label.BindProperty(n"tintColor", this.m_selected ? n"MainColors.Blue" : n"MainColors.Red");
98 | }
99 | }
100 |
101 | protected func TriggerChangeEvent() {
102 | let evt = new ItemSourceOptionChange();
103 | evt.value = this.m_value;
104 |
105 | this.QueueEvent(evt);
106 | }
107 |
108 | public func SetData(value: WardrobeItemSource, selected: Bool) {
109 | this.m_value = value;
110 | this.m_selected = selected;
111 |
112 | this.UpdateView();
113 | this.UpdateState();
114 | }
115 |
116 | public func GetValue() -> WardrobeItemSource {
117 | return this.m_value;
118 | }
119 |
120 | public func IsSelected() -> Bool {
121 | return this.m_selected;
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/scripts/UI/OutfitListData.reds:
--------------------------------------------------------------------------------
1 | module EquipmentEx
2 |
3 | enum OutfitListAction {
4 | Equip = 0,
5 | Unequip = 1,
6 | Save = 2
7 | }
8 |
9 | class OutfitListEntryData {
10 | public let Name: CName;
11 | public let Title: String;
12 | public let Color: CName;
13 | public let Action: OutfitListAction;
14 | public let Postition: Int32 = 2147483647;
15 | public let IsRemovable: Bool;
16 | public let IsSelectable: Bool;
17 | public let IsSelected: Bool;
18 | }
19 |
20 | class OutfitListDataView extends ScriptableDataView {
21 | public func UpdateView() {
22 | this.EnableSorting();
23 | this.Sort();
24 | this.DisableSorting();
25 | }
26 |
27 | public func SortItem(left: ref, right: ref) -> Bool {
28 | let leftEntry = left as OutfitListEntryData;
29 | let rightEntry = right as OutfitListEntryData;
30 |
31 | if leftEntry.Postition != rightEntry.Postition {
32 | return leftEntry.Postition < rightEntry.Postition;
33 | }
34 |
35 | return StrCmp(leftEntry.Title, rightEntry.Title) < 0;
36 | }
37 | }
38 |
39 | class OutfitListTemplateClassifier extends inkVirtualItemTemplateClassifier {
40 | // public func ClassifyItem(data: Variant) -> Uint32 {
41 | // return 0u;
42 | // }
43 | }
44 |
--------------------------------------------------------------------------------
/scripts/UI/OutfitListEntry.reds:
--------------------------------------------------------------------------------
1 | module EquipmentEx
2 |
3 | class OutfitListRefresh extends Event {}
4 |
5 | class OutfitListEntryClick extends Event {
6 | public let entry: ref;
7 | public let action: ref;
8 | }
9 |
10 | class OutfitListEntryHoverOver extends Event {
11 | public let entry: ref;
12 | }
13 |
14 | class OutfitListEntryHoverOut extends Event {
15 | public let entry: ref;
16 | }
17 |
18 | class OutfitListEntryController extends inkVirtualCompoundItemController {
19 | private let m_data: ref;
20 |
21 | private let m_root: wref;
22 | private let m_label: wref;
23 | private let m_checkbox: wref;
24 | private let m_selection: wref;
25 |
26 | private let m_isDisabled: Bool;
27 | private let m_isHovered: Bool;
28 |
29 | protected cb func OnInitialize() {
30 | this.m_root = this.GetRootCompoundWidget();
31 |
32 | this.m_label = this.GetChildWidgetByPath(n"titleAndCheckbox/FilterName") as inkText;
33 | this.m_label.SetStyle(r"base\\gameplay\\gui\\common\\main_colors.inkstyle");
34 |
35 | this.m_checkbox = this.GetChildWidgetByPath(n"titleAndCheckbox/checkbox");
36 | this.m_selection = this.GetChildWidgetByPath(n"titleAndCheckbox/checkbox/checkbox");
37 |
38 | this.RegisterToCallback(n"OnRelease", this, n"OnRelease");
39 | this.RegisterToCallback(n"OnEnter", this, n"OnHoverOver");
40 | this.RegisterToCallback(n"OnLeave", this, n"OnHoverOut");
41 | }
42 |
43 | protected cb func OnDataChanged(value: Variant) {
44 | this.m_data = FromVariant][>(value) as OutfitListEntryData;
45 |
46 | if IsDefined(this.m_data) {
47 | this.UpdateView();
48 | this.UpdateState();
49 | }
50 | }
51 |
52 | protected cb func OnRefresh(evt: ref) {
53 | if IsDefined(this.m_data) {
54 | this.UpdateView();
55 | this.UpdateState();
56 | }
57 | }
58 |
59 | protected cb func OnRelease(evt: ref) {
60 | if !this.m_isDisabled {
61 | this.TriggerClickEvent(evt.GetActionName());
62 | }
63 | }
64 |
65 | protected cb func OnHoverOver(evt: ref) {
66 | if !this.m_isDisabled {
67 | this.m_isHovered = true;
68 |
69 | this.UpdateState();
70 | this.TriggerHoverOverEvent();
71 | }
72 | }
73 |
74 | protected cb func OnHoverOut(evt: ref) {
75 | if !this.m_isDisabled {
76 | this.m_isHovered = false;
77 |
78 | this.UpdateState();
79 | this.TriggerHoverOutEvent();
80 | }
81 | }
82 |
83 | protected func UpdateView() {
84 | this.m_label.SetText(this.m_data.Title);
85 |
86 | if NotEquals(this.m_data.Color, n"") {
87 | this.m_label.BindProperty(n"tintColor", this.m_data.Color);
88 | } else {
89 | this.m_label.BindProperty(n"tintColor", n"MainColors.Red");
90 | }
91 |
92 | if this.m_data.IsSelectable {
93 | this.m_checkbox.SetVisible(true);
94 | this.m_selection.SetVisible(this.m_data.IsSelected);
95 | this.m_label.SetMargin(new inkMargin(30.0, 0.0, 0.0, 0.0));
96 | } else {
97 | this.m_checkbox.SetVisible(false);
98 | this.m_label.SetMargin(new inkMargin(10.0, 0.0, 0.0, 0.0));
99 | }
100 | }
101 |
102 | protected func UpdateState() {
103 | if this.m_isDisabled {
104 | this.m_root.SetState(n"Default");
105 | this.m_root.SetOpacity(0.3);
106 | } else {
107 | this.m_root.SetOpacity(1.0);
108 |
109 | if this.m_isHovered {
110 | this.m_root.SetState(n"Hover");
111 | }
112 | else {
113 | if this.m_data.IsSelectable && this.m_data.IsSelected {
114 | this.m_root.SetState(n"Selected");
115 | }
116 | else {
117 | this.m_root.SetState(n"Default");
118 | }
119 | }
120 | }
121 | }
122 |
123 | protected func TriggerClickEvent(action: ref) {
124 | let evt = new OutfitListEntryClick();
125 | evt.entry = this.m_data;
126 | evt.action = action;
127 |
128 | this.QueueEvent(evt);
129 | }
130 |
131 | protected func TriggerHoverOverEvent() {
132 | let evt = new OutfitListEntryHoverOver();
133 | evt.entry = this.m_data;
134 |
135 | this.QueueEvent(evt);
136 | }
137 |
138 | protected func TriggerHoverOutEvent() {
139 | let evt = new OutfitListEntryHoverOut();
140 | evt.entry = this.m_data;
141 |
142 | this.QueueEvent(evt);
143 | }
144 |
145 | // public final func PlayIntroAnimation(delay: Float) {
146 | // let animOptions: inkAnimOptions;
147 | // animOptions.executionDelay = delay;
148 | // this.PlayLibraryAnimation(n"OnFiltersListItem", animOptions);
149 | // }
150 | }
151 |
--------------------------------------------------------------------------------
/scripts/UI/OutfitManager.reds:
--------------------------------------------------------------------------------
1 | module EquipmentEx
2 |
3 | class OutfitManagerController extends inkLogicController {
4 | protected let m_player: wref;
5 | protected let m_outfitSystem: wref;
6 |
7 | protected let m_wardrobeScreen: wref;
8 | protected let m_buttonHints: wref;
9 |
10 | protected let m_outfitList: ref;
11 | protected let m_outfitListDataView: ref;
12 | protected let m_outfitListDataSource: ref;
13 | protected let m_outfitListTemplateClassifier: ref;
14 | protected let m_outfitListScroll: wref;
15 |
16 | protected let m_popupToken: ref;
17 | protected let m_popupOutfit: CName;
18 |
19 | protected let m_enabled: Bool;
20 |
21 | protected cb func OnInitialize() -> Bool {
22 | this.InitializeLayout();
23 | this.InitializeList();
24 | }
25 |
26 | public func Setup(outfitSystem: wref, wardrobeScreen: wref, buttonHints: wref) {
27 | this.m_outfitSystem = outfitSystem;
28 | this.m_wardrobeScreen = wardrobeScreen;
29 | this.m_buttonHints = buttonHints;
30 |
31 | this.PopulateList();
32 | this.SetEnabled(true);
33 | }
34 |
35 | public func SetEnabled(enabled: Bool) {
36 | this.m_enabled = enabled;
37 |
38 | let widget = this.GetRootWidget();
39 | widget.SetInteractive(this.m_enabled);
40 | widget.SetOpacity(this.m_enabled ? 1.0 : 0.6);
41 | }
42 |
43 | protected func InitializeLayout() {
44 | this.m_outfitListScroll = this.GetChildWidgetByPath(n"scroll_wrapper").GetControllerByType(n"inkScrollController") as inkScrollController;
45 |
46 | let scrollArea = this.GetChildWidgetByPath(n"scroll_wrapper/scroll_area");
47 | scrollArea.RegisterToCallback(n"OnScrollChanged", this, n"OnScrollChanged");
48 |
49 | let header = new inkVerticalPanel();
50 | header.SetName(n"header");
51 | header.SetChildMargin(new inkMargin(130.0, 0.0, 20.0, 0.0));
52 | header.Reparent(this.GetRootCompoundWidget());
53 |
54 | let title = new inkText();
55 | title = new inkText();
56 | title.SetName(n"title");
57 | title.SetText("LocKey#82878");
58 | title.SetFontFamily("base\\gameplay\\gui\\fonts\\raj\\raj.inkfontfamily");
59 | title.SetLetterCase(textLetterCase.UpperCase);
60 | title.SetStyle(r"base\\gameplay\\gui\\common\\main_colors.inkstyle");
61 | title.BindProperty(n"tintColor", n"MainColors.Red");
62 | title.BindProperty(n"fontStyle", n"MainColors.BodyFontWeight");
63 | title.BindProperty(n"fontSize", n"MainColors.ReadableFontSize");
64 | title.SetAnchor(inkEAnchor.TopLeft);
65 | title.SetMargin(new inkMargin(0.0, 0.0, 0.0, 4.0));
66 | title.Reparent(header);
67 |
68 | let divider = new inkRectangle();
69 | divider.SetName(n"divider");
70 | divider.SetMargin(new inkMargin(0.0, 0.0, 0.0, 15.0));
71 | divider.SetStyle(r"base\\gameplay\\gui\\common\\main_colors.inkstyle");
72 | divider.BindProperty(n"tintColor", n"MainColors.Red");
73 | divider.SetOpacity(0.3);
74 | divider.SetSize(800.0, 3.0);
75 | divider.Reparent(header);
76 | }
77 |
78 | protected func InitializeList() {
79 | this.m_outfitListDataSource = new ScriptableDataSource();
80 | this.m_outfitListDataView = new OutfitListDataView();
81 | this.m_outfitListDataView.SetSource(this.m_outfitListDataSource);
82 | this.m_outfitListTemplateClassifier = new OutfitListTemplateClassifier();
83 |
84 | this.m_outfitList = this.GetChildWidgetByPath(n"scroll_wrapper/scroll_area/outfit_list").GetController() as inkVirtualListController;
85 | this.m_outfitList.SetClassifier(this.m_outfitListTemplateClassifier);
86 | this.m_outfitList.SetSource(this.m_outfitListDataView);
87 | }
88 |
89 | protected func PopulateList() {
90 | let saveAction = new OutfitListEntryData();
91 | saveAction.Title = GetLocalizedTextByKey(n"UI-Wardrobe-SaveSet");
92 | saveAction.Color = n"MainColors.ActiveBlue";
93 | saveAction.Action = OutfitListAction.Save;
94 | saveAction.Postition = 1;
95 |
96 | let unequipAction = new OutfitListEntryData();
97 | unequipAction.Title = GetLocalizedTextByKey(n"UI-Wardrobe-NoOutfit");
98 | unequipAction.Action = OutfitListAction.Unequip;
99 | unequipAction.IsSelectable = true;
100 | unequipAction.IsSelected = !this.m_outfitSystem.IsActive();
101 | unequipAction.Postition = 2;
102 |
103 | this.m_outfitListDataSource.Clear();
104 | this.m_outfitListDataSource.AppendItem(saveAction);
105 | this.m_outfitListDataSource.AppendItem(unequipAction);
106 |
107 | for outfitName in this.m_outfitSystem.GetOutfits() {
108 | this.AppendToList(outfitName, false);
109 | }
110 |
111 | this.m_outfitListDataView.UpdateView();
112 | }
113 |
114 | protected func AppendToList(outfitName: CName, opt updateView: Bool) {
115 | let outfitEntry = new OutfitListEntryData();
116 | outfitEntry.Name = outfitName;
117 | outfitEntry.Title = NameToString(outfitName);
118 | outfitEntry.IsRemovable = true;
119 | outfitEntry.IsSelectable = true;
120 | outfitEntry.IsSelected = this.m_outfitSystem.IsEquipped(outfitEntry.Name);
121 |
122 | this.m_outfitListDataSource.AppendItem(outfitEntry);
123 |
124 | if updateView {
125 | this.m_outfitListDataView.UpdateView();
126 | }
127 | }
128 |
129 | protected func RemoveFromList(outfitName: CName, opt updateView: Bool) {
130 | for data in this.m_outfitListDataSource.GetArray() {
131 | let outfitEntry = data as OutfitListEntryData;
132 | if Equals(outfitEntry.Name, outfitName) {
133 | this.m_outfitListDataSource.RemoveItem(outfitEntry);
134 |
135 | if updateView {
136 | this.m_outfitListDataView.UpdateView();
137 | }
138 |
139 | break;
140 | }
141 | }
142 | }
143 |
144 | protected func RefreshList(opt updateState: Bool) {
145 | if updateState {
146 | for data in this.m_outfitListDataSource.GetArray() {
147 | let outfitEntry = data as OutfitListEntryData;
148 | if outfitEntry.IsSelectable {
149 | outfitEntry.IsSelected = this.m_outfitSystem.IsEquipped(outfitEntry.Name);
150 | }
151 | }
152 | }
153 |
154 | this.QueueEvent(new OutfitListRefresh());
155 | }
156 |
157 | protected cb func OnOutfitListEntryClick(evt: ref) {
158 | if !this.m_enabled {
159 | return;
160 | }
161 |
162 | if evt.action.IsAction(n"click") && this.AccessOutfitSystem() {
163 | this.PlaySound(n"Button", n"OnPress");
164 |
165 | switch evt.entry.Action {
166 | case OutfitListAction.Equip:
167 | this.m_outfitSystem.LoadOutfit(evt.entry.Name);
168 | break;
169 | case OutfitListAction.Unequip:
170 | this.m_outfitSystem.Deactivate();
171 | break;
172 | case OutfitListAction.Save:
173 | this.ShowSaveOutfitPopup();
174 | break;
175 | }
176 |
177 | this.ShowButtonHints(evt.entry);
178 | return;
179 | }
180 |
181 | if evt.action.IsAction(n"drop_item") && Equals(evt.entry.Action, OutfitListAction.Equip) && this.AccessOutfitSystem() {
182 | this.ShowDeleteOutfitPopup(evt.entry.Name);
183 | }
184 | }
185 |
186 | protected cb func OnOutfitListEntryItemHoverOver(evt: ref) {
187 | this.ShowButtonHints(evt.entry);
188 | }
189 |
190 | protected cb func OnOutfitListEntryItemHoverOut(evt: ref) {
191 | this.ShowButtonHints(null);
192 | }
193 |
194 | protected cb func ShowSaveOutfitPopup() {
195 | this.m_popupToken = GenericMessageNotification.ShowInput(this.m_wardrobeScreen, GetLocalizedTextByKey(n"UI-Wardrobe-SaveSet"), GetLocalizedTextByKey(n"UI-Wardrobe-NotificationSaveSet"), GenericMessageNotificationType.ConfirmCancel);
196 | this.m_popupToken.RegisterListener(this, n"OnSaveOutfitPopupClosed");
197 | }
198 |
199 | protected cb func OnSaveOutfitPopupClosed(data: ref) {
200 | let resultData = data as GenericMessageNotificationCloseData;
201 |
202 | if Equals(resultData.result, GenericMessageNotificationResult.Confirm) && NotEquals(resultData.input, "") {
203 | let outfitName = StringToName(resultData.input);
204 |
205 | if this.m_outfitSystem.HasOutfit(outfitName) {
206 | this.ShowReplaceOutfitPopup(outfitName);
207 | return;
208 | }
209 |
210 | this.PlaySound(n"Item", n"OnBuy");
211 |
212 | if this.m_outfitSystem.SaveOutfit(outfitName, true) {
213 | this.AppendToList(outfitName, true);
214 | }
215 | }
216 |
217 | this.ResetPopupState();
218 | }
219 |
220 | protected cb func ShowReplaceOutfitPopup(outfitName: CName) {
221 | this.m_popupOutfit = outfitName;
222 | this.m_popupToken = GenericMessageNotification.Show(this.m_wardrobeScreen, GetLocalizedTextByKey(n"UI-Wardrobe-SaveSet"), GetLocalizedTextByKey(n"UI-Wardrobe-NotificationReplaceSet"), GenericMessageNotificationType.ConfirmCancel);
223 | this.m_popupToken.RegisterListener(this, n"OnReplaceOutfitPopupClosed");
224 | }
225 |
226 | protected cb func OnReplaceOutfitPopupClosed(data: ref) {
227 | let resultData = data as GenericMessageNotificationCloseData;
228 |
229 | if Equals(resultData.result, GenericMessageNotificationResult.Confirm) {
230 | this.PlaySound(n"Item", n"OnBuy");
231 |
232 | if this.m_outfitSystem.SaveOutfit(this.m_popupOutfit, true) {
233 | this.RefreshList(true);
234 | }
235 | }
236 |
237 | this.ResetPopupState();
238 | }
239 |
240 | protected cb func ShowDeleteOutfitPopup(outfitName: CName) {
241 | this.m_popupOutfit = outfitName;
242 | this.m_popupToken = GenericMessageNotification.Show(this.m_wardrobeScreen, GetLocalizedTextByKey(n"UI-Wardrobe-Deleteset"), GetLocalizedTextByKey(n"UI-Wardrobe-NotificationDeleteSet"), GenericMessageNotificationType.ConfirmCancel);
243 | this.m_popupToken.RegisterListener(this, n"OnDeleteOutfitPopupClosed");
244 | }
245 |
246 | protected cb func OnDeleteOutfitPopupClosed(data: ref) {
247 | let resultData = data as GenericMessageNotificationCloseData;
248 |
249 | if Equals(resultData.result, GenericMessageNotificationResult.Confirm) {
250 | this.PlaySound(n"Item", n"OnDisassemble");
251 |
252 | if this.m_outfitSystem.DeleteOutfit(this.m_popupOutfit) {
253 | this.RemoveFromList(this.m_popupOutfit, true);
254 | }
255 | }
256 |
257 | this.ResetPopupState();
258 | }
259 |
260 | protected func ResetPopupState() {
261 | this.m_popupOutfit = n"";
262 | this.m_popupToken = null;
263 | }
264 |
265 | protected func ShowButtonHints(entry: wref) {
266 | this.m_buttonHints.RemoveButtonHint(n"click");
267 | this.m_buttonHints.RemoveButtonHint(n"drop_item");
268 |
269 | if IsDefined(entry) {
270 | if entry.IsRemovable {
271 | this.m_buttonHints.AddButtonHint(n"drop_item", GetLocalizedTextByKey(n"UI-Wardrobe-Deleteset"));
272 | }
273 |
274 | if entry.IsSelectable && !entry.IsSelected {
275 | this.m_buttonHints.AddButtonHint(n"click", GetLocalizedTextByKey(n"Gameplay-Devices-Interactions-Equip"));
276 | }
277 | }
278 | }
279 |
280 | protected cb func OnOutfitUpdated(evt: ref) {
281 | this.RefreshList(true);
282 | }
283 |
284 | protected cb func OnOutfitPartUpdated(evt: ref) {
285 | this.RefreshList(true);
286 | }
287 |
288 | protected cb func OnOutfitListUpdated(evt: ref) {
289 | this.PopulateList();
290 | }
291 |
292 | protected cb func OnScrollChanged(value: Vector2) {
293 | this.RefreshList();
294 | }
295 |
296 | protected func AccessOutfitSystem() -> Bool {
297 | if this.m_outfitSystem.IsBlocked() {
298 | let notification = new UIMenuNotificationEvent();
299 | notification.m_notificationType = UIMenuNotificationType.InventoryActionBlocked;
300 | this.QueueEvent(notification);
301 |
302 | return false;
303 | }
304 |
305 | return true;
306 | }
307 | }
308 |
--------------------------------------------------------------------------------
/scripts/UI/OutfitMappingPopup.reds:
--------------------------------------------------------------------------------
1 | module EquipmentEx
2 | import Codeware.UI.*
3 |
4 | public class OutfitMappingPopup extends InMenuPopup {
5 | private let m_itemID: ItemID;
6 | private let m_slotID: TweakDBID;
7 | private let m_system: ref;
8 | private let m_options: array>;
9 | private let m_arranged: Bool;
10 |
11 | protected cb func OnCreate() {
12 | super.OnCreate();
13 |
14 | let content = InMenuPopupContent.Create();
15 | content.SetTitle(this.m_system.GetItemName(this.m_itemID));
16 | content.Reparent(this);
17 |
18 | let panel = new inkHorizontalPanel();
19 | panel.SetMargin(new inkMargin(0, 24, 0, 0));
20 | panel.Reparent(content.GetContainerWidget());
21 |
22 | let outfitSlots = OutfitConfig.OutfitSlots();
23 |
24 | let schema = [
25 | [n"Head", n"Face", n"Ears", n"Neck"],
26 | [n"Torso", n"Back", n"Waist", n"Body"],
27 | [n"Arms", n"Hands", n"Fingers"],
28 | [n"Legs", n"Feet", n"Toes"]
29 | ];
30 |
31 | for areas in schema {
32 | let column = new inkVerticalPanel();
33 | column.Reparent(panel);
34 |
35 | for area in areas {
36 | for outfitSlot in outfitSlots {
37 | if Equals(outfitSlot.slotArea, area) {
38 | let option = this.SpawnOption(column);
39 | option.SetData(outfitSlot, Equals(this.m_slotID, outfitSlot.slotID));
40 |
41 | ArrayPush(this.m_options, option);
42 | }
43 | }
44 |
45 | if NotEquals(area, ArrayLast(areas)) {
46 | let divider = new inkCanvas();
47 | divider.SetMargin(0, 0, 0, 40);
48 | divider.Reparent(column);
49 | }
50 | }
51 | }
52 |
53 | let footer = InMenuPopupFooter.Create();
54 | footer.Reparent(this);
55 |
56 | let confirmBtn = PopupButton.Create();
57 | confirmBtn.SetText(GetLocalizedTextByKey(n"UI-UserActions-Equip")); // GetLocalizedText("LocKey#23123")
58 | confirmBtn.SetInputAction(n"one_click_confirm");
59 | confirmBtn.Reparent(footer);
60 |
61 | let cancelBtn = PopupButton.Create();
62 | cancelBtn.SetText(GetLocalizedText("LocKey#22175"));
63 | cancelBtn.SetInputAction(n"cancel");
64 | cancelBtn.Reparent(footer);
65 | }
66 |
67 | protected func SpawnOption(parent: ref) -> ref {
68 | let widget = this.SpawnFromExternal(parent, r"equipment_ex\\gui\\wardrobe.inkwidget",
69 | n"OutfitListEntry:EquipmentEx.OutfitSlotOptionController");
70 | return widget.GetController() as OutfitSlotOptionController;
71 | }
72 |
73 | protected cb func OnArrangeChildrenComplete() {
74 | if !this.m_arranged {
75 | for option in this.m_options {
76 | option.UpdateView();
77 | }
78 |
79 | this.m_arranged = true;
80 | }
81 | }
82 |
83 | protected cb func OnChange(evt: ref) {
84 | this.m_slotID = evt.slotID;
85 | }
86 |
87 | protected cb func OnConfirm() {
88 | this.m_system.AssignItem(this.m_itemID, this.m_slotID);
89 |
90 | if !this.m_system.IsEquipped(this.m_itemID) {
91 | this.m_system.EquipItem(this.m_itemID);
92 | }
93 | }
94 |
95 | public static func Show(requester: ref, itemID: ItemID, system: ref) {
96 | let popup = new OutfitMappingPopup();
97 | popup.m_itemID = itemID;
98 | popup.m_slotID = system.GetItemSlot(itemID);
99 | popup.m_system = system;
100 | popup.Open(requester);
101 | }
102 |
103 | func OnCancel() {}
104 | func OnShown() {}
105 | func OnReparent(parent: ref) {}
106 | }
107 |
--------------------------------------------------------------------------------
/scripts/UI/OutfitSlotOption.reds:
--------------------------------------------------------------------------------
1 | module EquipmentEx
2 |
3 | class OutfitSlotOptionChange extends Event {
4 | public let slotID: TweakDBID;
5 | }
6 |
7 | class OutfitSlotOptionController extends inkButtonController {
8 | private let m_data: ExtraSlotConfig;
9 |
10 | private let m_root: wref;
11 | private let m_label: wref;
12 | private let m_checkbox: wref;
13 | private let m_selection: wref;
14 |
15 | private let m_disabled: Bool;
16 | private let m_hovered: Bool;
17 | private let m_selected: Bool;
18 |
19 | protected cb func OnInitialize() {
20 | this.m_root = this.GetRootCompoundWidget();
21 |
22 | this.m_label = this.GetChildWidgetByPath(n"titleAndCheckbox/FilterName") as inkText;
23 | this.m_label.SetStyle(r"base\\gameplay\\gui\\common\\main_colors.inkstyle");
24 |
25 | this.m_checkbox = this.GetChildWidgetByPath(n"titleAndCheckbox/checkbox");
26 | this.m_selection = this.GetChildWidgetByPath(n"titleAndCheckbox/checkbox/checkbox");
27 |
28 | this.RegisterToCallback(n"OnRelease", this, n"OnRelease");
29 | this.RegisterToCallback(n"OnEnter", this, n"OnHoverOver");
30 | this.RegisterToCallback(n"OnLeave", this, n"OnHoverOut");
31 | }
32 |
33 | protected cb func OnRelease(evt: ref) {
34 | if evt.IsAction(n"click") && !this.m_disabled && !this.m_selected {
35 | this.TriggerChangeEvent();
36 | }
37 | }
38 |
39 | protected cb func OnHoverOver(evt: ref) {
40 | if !this.m_disabled {
41 | this.m_hovered = true;
42 |
43 | this.UpdateState();
44 | // this.TriggerHoverOverEvent();
45 | }
46 | }
47 |
48 | protected cb func OnHoverOut(evt: ref) {
49 | if !this.m_disabled {
50 | this.m_hovered = false;
51 |
52 | this.UpdateState();
53 | // this.TriggerHoverOutEvent();
54 | }
55 | }
56 |
57 | protected cb func OnOptionChange(evt: ref]