Skip to content

Commit 59d8fea

Browse files
committed
update
1 parent 6ae7bbe commit 59d8fea

File tree

11 files changed

+144
-51
lines changed

11 files changed

+144
-51
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ This mod has been inspired by 'Copy Any Outfit' by scumbumbo. All code has been
201201
# Addendum
202202

203203
## Game compatibility
204-
This mod has been tested with `The Sims 4` 1.110.311, S4CL 3.9, TS4Lib 0.3.32.
204+
This mod has been tested with `The Sims 4` 1.111.102, S4CL 3.9, TS4Lib 0.3.32.
205205
It is expected to be compatible with many upcoming releases of TS4, S4CL and TS4Lib.
206206

207207
## Dependencies

_TS4/Mods/_o19_/copy_outfits.package

666 Bytes
Binary file not shown.

_TS4/mod_documentation/copy_outfits/README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ This mod has been inspired by 'Copy Any Outfit' by scumbumbo. All code has been
201201
# Addendum
202202

203203
## Game compatibility
204-
This mod has been tested with `The Sims 4` 1.110.311, S4CL 3.9, TS4Lib 0.3.32.
204+
This mod has been tested with `The Sims 4` 1.111.102, S4CL 3.9, TS4Lib 0.3.32.
205205
It is expected to be compatible with many upcoming releases of TS4, S4CL and TS4Lib.
206206

207207
## Dependencies
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
CopyOutfits v1.9.11 for The Sims 4 v1.110.311 and S4CL v3.9
2-
Created on 2024-11-26
3-
v1.9.11
4-
Fix generate_outfit()
1+
CopyOutfits v1.9.13 for The Sims 4 v1.111.102 and S4CL v3.9
2+
Created on 2024-12-07
3+
v1.9.13
4+
Add PieMenu More > Fix Head
55

copy_outfits/copy_outfits_mannequin.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
log.enable()
4040

4141

42-
class CopyOutfitsMannequin: # (metaclass=Singleton):
42+
class CopyOutfitsMannequin(metaclass=Singleton):
4343
def __init__(self):
4444
self.guids = {
4545
CopyOutfitsAge.TYAE: {

copy_outfits/enums/pie_menu_action.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,8 @@ class PieMenuAction(CommonEnum):
2020
BODY_TYPE = 128 # TS4 CAS parts as defined in ts4lib.BodyType(CommonEnum) ea.outfits.BodyType(Enum)
2121

2222
SKIN = 256 # Apply next head/skin to mannequin
23-
MANNEQUIN = 512 # Add mannequin
23+
MANNEQUIN = 512 # Add mannequin
24+
25+
RESERVED = 1024 # free
26+
27+
FIX_HEAD = 2048

copy_outfits/main.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -112,8 +112,9 @@ def on_started(self, interaction_sim: Sim, interaction_target: Any, instance_tun
112112
elif _action == PieMenuAction.ADD:
113113
OutfitAdd().add_outfits(self._sim)
114114
elif _action == PieMenuAction.BODY_TYPE:
115-
116115
log.debug(f"TODO - BODY_TYPE Picker")
116+
elif _action == PieMenuAction.FIX_HEAD:
117+
OutfitSkin().fix_head(self.interaction_target)
117118
else:
118119
log.warn(f"Unknown action '{_action}'. This should never happen.")
119120
except Exception as e:

copy_outfits/modinfo.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ def _file_path(self) -> str:
2828

2929
@property
3030
def _version(self) -> str:
31-
return '1.9.11'
31+
return '1.9.13'
3232

3333

3434
r'''
@@ -42,6 +42,10 @@ def _version(self) -> str:
4242
TODO
4343
Load and Save outfits
4444
Read config files / support user defined skins
45+
v1.9.13
46+
Add PieMenu More > Fix Head
47+
v1.9.12
48+
Add 'o19.co.fix' to apply the vanilla head to Child, Teen-Elder sims
4549
v1.9.11
4650
Fix generate_outfit()
4751
v1.9.10

copy_outfits/outfit_skin.py

+103-7
Original file line numberDiff line numberDiff line change
@@ -2,30 +2,78 @@
22
# LICENSE https://creativecommons.org/licenses/by/4.0/ https://creativecommons.org/licenses/by/4.0/legalcode
33
# © 2024 https://github.com/Oops19
44
#
5+
6+
57
import random
8+
import sys
9+
import threading
10+
import traceback
11+
from typing import Dict, Set
612

13+
import services
714
from copy_outfits.enums.copy_outfits_age import CopyOutfitsAge
815
from copy_outfits.enums.default_head import DefaultHead
916
from copy_outfits.enums.pie_menu_action_id import PieMenuActionId
1017
from copy_outfits.modinfo import ModInfo
1118
from copy_outfits.persist.skin_store import SkinStore
1219
from copy_outfits.struct.copy_outfits_sim import CopyOutfitsSim
20+
from objects import HiddenReasonFlag, ALL_HIDDEN_REASONS
21+
from sims.sim_info import SimInfo
22+
from sims4communitylib.services.commands.common_console_command import CommonConsoleCommand
23+
from sims4communitylib.services.commands.common_console_command_output import CommonConsoleCommandOutput
1324
from sims4communitylib.services.sim.cas.common_sim_outfit_io import CommonSimOutfitIO
25+
from sims4communitylib.utils.cas.common_cas_utils import CommonCASUtils
1426
from sims4communitylib.utils.cas.common_outfit_utils import CommonOutfitUtils
1527
from sims4communitylib.utils.common_log_registry import CommonLog, CommonLogRegistry
28+
from sims4communitylib.utils.sims.common_age_utils import CommonAgeUtils
29+
from sims4communitylib.utils.sims.common_gender_utils import CommonGenderUtils
30+
from sims4communitylib.utils.sims.common_sim_utils import CommonSimUtils
31+
from ts4lib.common_enums.body_type import BodyType
1632
from ts4lib.utils.singleton import Singleton
1733

1834
log: CommonLog = CommonLogRegistry.get().register_log(ModInfo.get_identity(), 'OutfitSkin')
1935
log.enable()
2036

2137

2238
class OutfitSkin(metaclass=Singleton):
23-
""" Add skins to sims """
39+
do_init = True
40+
2441
def __init__(self):
25-
self.config = {
42+
log.debug(f"{OutfitSkin.do_init}")
43+
if OutfitSkin.do_init is True:
44+
OutfitSkin.do_init = False
45+
self.init()
46+
47+
""" Add skins to sims """
48+
def init(self):
49+
log.debug(f"init")
50+
try:
51+
log.debug(f"Thread Dump")
52+
for thread in threading.enumerate():
53+
log.debug(f"{thread}")
54+
thread_details = traceback.extract_stack(sys._current_frames()[thread.ident])
55+
for thread_detail in thread_details:
56+
(filename, number, function, line_text) = thread_detail
57+
log.debug(f" {filename}#{number} '{line_text}' in '{function}()'")
58+
log.debug(f"")
59+
except:
60+
pass
61+
62+
self.available_skins: Dict = {
63+
CopyOutfitsAge.TYAE: {
64+
True: [], # Female / is_female==True
65+
False: [], # Male
66+
},
67+
CopyOutfitsAge.CHILD: {
68+
True: [], # Female
69+
False: [], # Male
70+
},
71+
}
72+
self.supported_skins: Dict = {
2673
CopyOutfitsAge.TYAE: {
2774
True: [ # is_female, used in o19_yfHead.package
2875
# The high-bit must be set for all custom CAS Parts. Otherwise, they will not be found.
76+
# custom head cas part, custom skin images, description
2977
(0xFD57E11C9F0D2AFD, 0xA83ABE943B90DD44, 'ddarkpinkrosa0_Miami_Skin_V1'),
3078
(0xE2F29393763FBEFD, 0xE9DF3FAEC660DE19, 'ddarkpinkrosa0_Miami_Skin_V1'),
3179
(0xF2157D9C0F5DBB0C, 0xE1750D34ED49C938, 'ddarkpinkrosa0_Miami_Skin_V1'),
@@ -36,7 +84,6 @@ def __init__(self):
3684
(0x8B269F3A852B0059, 0xC42933F4CFD6B686, 'JS_008Skin'),
3785
(0x8F30F85CC19644C1, 0xDABE010DB300612C, 'JS_008Skin'),
3886
(0x8D88C11365FFA9EC, 0xA91D148D8D6709B5, 'JS_008Skin'),
39-
4087
(0xCD27D0CC83CC79CE, 0xA2C37B6FF654696D, '[THISISTHEM] Pamela Anderson Skin'),
4188
(0x8111880154582F44, 0xBF054E412D1C3FF1, '[THISISTHEM] Selena G. Skin'),
4289
(0xD8191C563F223169, 0x945804D533C7195C, '[THISISTHEM] Salma Hayek Skin'),
@@ -53,7 +100,6 @@ def __init__(self):
53100
(0xF5E803E374BAAA88, 0xC6E085AE73312D6D, '[THISISTHEM] Ashley Skin'),
54101
(0xA59BBF6B64B413FF, 0xAD660A57BEC2E456, '[THISISTHEM] Abény Skin'),
55102
(DefaultHead.YF_HEAD_DEFAULT.value, DefaultHead.YF_HEAD_DEFAULT.value, 'TS4 Default'),
56-
57103
],
58104
False: [ # is_male, used in o19_ymHead.package
59105
(0xE66AA85AD5A6C480, 0xA63963459FAC2D01, '[THISISTHEM] A$AP Rocky Skin'),
@@ -77,10 +123,35 @@ def __init__(self):
77123
(0x800298CD9078A60D, 0xA78520CC00C7191C, '[THISISTHEM] Child Skin N24'),
78124
(DefaultHead.CU_HEAD_DEFAULT.value, DefaultHead.CU_HEAD_DEFAULT.value, 'TS4 Default'),
79125
],
80-
81126
},
82127
}
83-
self.skin_store = SkinStore(self.config)
128+
129+
missing_packages: Set = set()
130+
for sim_age in [CopyOutfitsAge.CHILD, CopyOutfitsAge.TYAE, ]:
131+
_ages = {}
132+
for is_female in [True, False, ]:
133+
_available_skins = []
134+
for data in self.supported_skins.get(sim_age).get(is_female):
135+
check_cas_part, use_cas_part, package_name = data
136+
if CommonCASUtils.is_cas_part_loaded(check_cas_part):
137+
if CommonCASUtils.is_cas_part_loaded(use_cas_part):
138+
_available_skins.append(use_cas_part)
139+
else:
140+
# this should never happen
141+
log.warn(f"copy_outfits.package missing or broken - {use_cas_part:016X} not found!")
142+
else:
143+
missing_packages.add(package_name)
144+
log.warn(f"Dropping: {is_female} {sim_age}: {data}")
145+
_ages.update({is_female: _available_skins})
146+
self.available_skins.update({sim_age: _ages})
147+
148+
if missing_packages:
149+
log.info(f"Optional packages which are not installed: '{missing_packages}'. Install some of them to use more skins on mannequins.")
150+
151+
log.debug(f".... {self.available_skins}")
152+
# Initialize store with the available skins
153+
self.skin_store = SkinStore(self.available_skins)
154+
log.debug(f"init-end")
84155

85156
# A78520CC00C7191C
86157
def apply_skin(self, zim: CopyOutfitsSim):
@@ -116,6 +187,31 @@ def apply_skin(self, zim: CopyOutfitsSim):
116187
sim_info._current_outfit = (outfit_category, outfit_index)
117188
log.debug(f"Replacing head {current_head} with new {head_skin}.")
118189

190+
def fix_head(self, sim_info: SimInfo):
191+
sim_info = CommonSimUtils.get_sim_info(sim_info)
192+
if not sim_info:
193+
return
194+
if CommonAgeUtils.is_child(sim_info):
195+
sim_age = CopyOutfitsAge.CHILD
196+
elif CommonAgeUtils.is_teen_adult_or_elder(sim_info):
197+
sim_age = CopyOutfitsAge.TYAE
198+
else:
199+
log.info(f"Age not supported.")
200+
return
201+
is_female = CommonGenderUtils.is_female(sim_info)
202+
old_head_id = CommonCASUtils.get_cas_part_id_at_body_type(sim_info, BodyType.HEAD.value)
203+
# True == CommonCASUtils.is_cas_part_loaded(old_head_id) as the part is 'in use'. A test makes no sense.
204+
head_ids = OutfitSkin().available_skins.get(sim_age).get(is_female)
205+
head_id = random.choice(head_ids)
206+
207+
log.info(f"Fixing '{sim_info}'s head to {head_id} (from {old_head_id}).")
208+
CommonCASUtils.attach_cas_part_to_sim(sim_info, head_id, BodyType.HEAD.value)
209+
210+
@staticmethod
211+
@CommonConsoleCommand(ModInfo.get_identity(), 'o19.co.fix', 'Fix the head of the active sim.')
212+
def cheat_o19_test_print_genetics(output: CommonConsoleCommandOutput):
213+
sim_info = CommonSimUtils.get_active_sim_info()
214+
OutfitSkin().fix_head(sim_info)
119215

120-
# Init SkinStore as soon as possible
216+
# Init OutfitSkin / SkinStore as soon as possible
121217
OutfitSkin()

copy_outfits/persist/skin_store.py

+21-34
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99

1010
from copy_outfits.enums.copy_outfits_age import CopyOutfitsAge
1111
from copy_outfits.modinfo import ModInfo
12-
from sims4communitylib.utils.cas.common_cas_utils import CommonCASUtils
1312
from sims4communitylib.utils.common_log_registry import CommonLog, CommonLogRegistry
1413
from ts4lib.utils.singleton import Singleton
1514

@@ -19,6 +18,7 @@
1918

2019
class SkinStore(metaclass=Singleton):
2120
def __init__(self, config: Dict):
21+
log.debug(f'init')
2222
self.index = {
2323
CopyOutfitsAge.TYAE: {
2424
True: 0,
@@ -29,16 +29,19 @@ def __init__(self, config: Dict):
2929
False: 0,
3030
},
3131
}
32-
self.config = {
33-
CopyOutfitsAge.TYAE: {
34-
True: [],
35-
False: [],
36-
},
37-
CopyOutfitsAge.CHILD: {
38-
True: [],
39-
False: [],
40-
},
41-
}
32+
if config:
33+
self.config = config
34+
else:
35+
self.config = {
36+
CopyOutfitsAge.TYAE: {
37+
True: [],
38+
False: [],
39+
},
40+
CopyOutfitsAge.CHILD: {
41+
True: [],
42+
False: [],
43+
},
44+
}
4245
self.size = {
4346
CopyOutfitsAge.TYAE: {
4447
True: 0,
@@ -49,30 +52,14 @@ def __init__(self, config: Dict):
4952
False: 0,
5053
},
5154
}
52-
missing_packages: Set = set()
53-
for age, gender_data in config.items():
54-
gender_size = {}
55-
for gender, cas_parts in gender_data.items():
56-
existing_cas_parts = []
57-
for cas_part in cas_parts:
58-
check_cas_part, use_cas_part, package_name = cas_part
59-
if CommonCASUtils.is_cas_part_loaded(check_cas_part):
60-
if CommonCASUtils.is_cas_part_loaded(use_cas_part):
61-
existing_cas_parts.append(use_cas_part)
62-
else:
63-
log.warn(f"copy_outfits.package missing or broken - {use_cas_part:016X} not found!")
64-
else:
65-
missing_packages.add(package_name)
66-
log.warn(f"Dropping: {cas_part}")
67-
gender_data.update({gender: existing_cas_parts})
68-
gender_size.update({gender: len(existing_cas_parts)})
69-
self.config.update({age: gender_data})
70-
self.size.update({age: gender_size})
7155

72-
if missing_packages:
73-
log.info(f"Optional packages which are not installed: '{missing_packages}'. Install some of them to use more skins on mannequins.")
74-
log.debug(f"self.size = {self.size}")
75-
log.debug(f"self.config = {self.config}")
56+
for sim_age in [CopyOutfitsAge.CHILD, CopyOutfitsAge.TYAE, ]:
57+
_age_sizes = {}
58+
for is_female in [True, False, ]:
59+
_age_sizes.update({is_female: len(config.get(sim_age).get(is_female))})
60+
self.size.update({sim_age: _age_sizes})
61+
log.debug(f"size = {self.size}")
62+
log.debug(f"config = {self.config}")
7663

7764
def get_skin(self, age: CopyOutfitsAge, is_female: bool = True, offset: int = 1, random_offset: bool = False):
7865
size = self.size.get(age).get(is_female)

copy_outfits/pie/interactions_reg.py

+1
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ def interactions_to_add(self) -> Tuple[int]:
101101
0xC11F89ED9D7FF93A, # 'Destroy' - fnv('o19_Copy_Outfits_PMC__Copy_Outfits__Mannequin__Remove_PMA_Destroy_debug')
102102

103103
0x4B864F684F7FC787, # 'Print Clipboard' - fnv('o19_Copy_Outfits_PMC__Copy_Outfits__More_PMA_Print_Clipboard_debug')
104+
0x9D595705F4525479, # 'Fix Head' - fnv('o19_Copy_Outfits_PMC__Copy_Outfits__More_PMA_Fix_Head_debug')
104105
0x5BA6F113C0F2661A, # 'Clear All' - fnv('o19_Copy_Outfits_PMC__Copy_Outfits__More_PMA_Clear_All_debug')
105106
0xA88C83FEBA17597D, # 'Clipboard 0' - fnv('o19_Copy_Outfits_PMC__Copy_Outfits__More__Clear_PMA_Clipboard_0_debug')
106107
0x71BE7E2EB047630C, # 'Clipboard 1' - fnv('o19_Copy_Outfits_PMC__Copy_Outfits__More__Clear_PMA_Clipboard_1_debug')

0 commit comments

Comments
 (0)