From 923ba14d61d9992d0612d72fa3eec257f55406ad Mon Sep 17 00:00:00 2001 From: aerinon Date: Fri, 14 Nov 2025 09:05:12 -0700 Subject: [PATCH] feat: force_enemy feature feat: rom-side enemy "spies" --- BaseClasses.py | 1 + Main.py | 9 ++++++++- Rom.py | 2 +- data/base2current.bps | Bin 118123 -> 118196 bytes source/classes/CustomSettings.py | 8 ++++++++ source/enemizer/Enemizer.py | 17 +++++++++++------ source/enemizer/SpriteSheets.py | 6 ++++-- 7 files changed, 33 insertions(+), 10 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index 529eca39..6a12f32e 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -129,6 +129,7 @@ class World(object): set_player_attr('enemy_health', 'default') set_player_attr('enemy_damage', 'default') set_player_attr('any_enemy_logic', 'allow_all') + set_player_attr('force_enemy', None) set_player_attr('beemizer', '0') set_player_attr('escape_assist', []) set_player_attr('crystals_needed_for_ganon', 7) diff --git a/Main.py b/Main.py index 3c4d8c9b..197602e2 100644 --- a/Main.py +++ b/Main.py @@ -38,7 +38,7 @@ from source.enemizer.DamageTables import DamageTable from source.enemizer.Enemizer import randomize_enemies from source.rom.DataTables import init_data_tables -version_number = '1.4.11' +version_number = '1.5.0' version_branch = '-u' __version__ = f'{version_number}{version_branch}' @@ -170,6 +170,13 @@ def main(args, seed=None, fish=None): world.rom_seeds = {player: random.randint(0, 999999999) for player in range(1, world.players + 1)} world.finish_init() + # custom settings - these haven't been promoted to full settings yet + in_progress_settings = ['force_enemy'] + for player in range(1, world.players + 1): + for setting in in_progress_settings: + if world.customizer and world.customizer.has_setting(player, setting): + getattr(world, setting)[player] = world.customizer.get_setting(player, setting) + logger.info( world.fish.translate("cli","cli","app.title") + "\n", __version__, diff --git a/Rom.py b/Rom.py index fc343d7e..04987530 100644 --- a/Rom.py +++ b/Rom.py @@ -42,7 +42,7 @@ from source.enemizer.Enemizer import write_enemy_shuffle_settings JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = 'f0881c7ccde2f0d0cd5e9cfe481633ad' +RANDOMIZERBASEHASH = '1e87ad01a54f1c15e2ec16a79a9bcc20' class JsonRom(object): diff --git a/data/base2current.bps b/data/base2current.bps index e919637df15e2adc47a00eb2383ceb334378988b..a287a08d9090504aa003b5e346d481fe5fdad888 100644 GIT binary patch delta 1216 zcmWlXZA?>V7>3{X@YP`{trS{Xz*77eA1bm4eod*UYjH3~rYImVQQEN~0aS`Oc@MR< zr&@|VmUqz6ZmHs|vMzK=w@zAGRuM7y$7Da8)6Hd?nUu_I&WWDYU(bEt$@Sz(p8LL-DAqLZltqagIcQjG(WzpETA)R$V5ITn%ZQr+FfJr@DeQ z;7_QR=2BNRPW6~m4Zq$3o5uEBgKY%t#4|ovSgKN~TrucEHfyebXT|-I8ox&?O?#-T z33{|v{`+?>#=mwK7uM9=)zN3ED^8os-5cAAj5Xz>HhPju7`63xbo7~4pL&27_+dtL z>Q+9&d4AYO)MnvvKTIbMs*V+EgFapcPI z?~^cC+4j+6+Z)NJ=tZrW$Y2O^RPhMQn*JF+O_#SMAaa!>@wBzdZGJR)C#k4-@_p{T|KQ?MXj zm@`WW!3KBkQha9$YNY&4eB?aTe#xwq+o?~%3wPrW&VzD0U8nL;Uf^{ft;0ueK*{>m zZ_nCG(`N@o3fnMGVH-@4X))Bf1m%>G0VEhpr*PX|%FL0EdW$x2kZ(;b2L(!EF4OJR z_|6Tuk!W0klc%9XtZZ9=@IF2potXpwftRkqE+iWXND(cL#XF{9Ex)+>dm|fUfIy!G z#DU|$=#a^AU>YijatThj30Gvrump&z156aTc~=SwEXy&N*S6gtpVB!#V-RS7z2i+> zdlMe(-mr2Z3iNu#-V6vxkVK?+i99py!+P&!ZnhmOVS6sNTs%QkhQf+9l}KpH?`q5}r%b1hUZ zT8h1vGpSIQPB5@pwKFg}ty~x~UIN+nVc7@Widp#bCO?v$@$VQ z`Kw)Wg$W2W4B%#WP(XoV;U&vQfi&I7nxv;#bH0)7xW}60oTbF%Hb(Xak$LXrv9AV; zW#{6$3}&{;JzbF(9F%avl7doxWsQodAbfUBX24n2WV~9_ceRFG-bAAM@^6qs4B3I- zdx<8w?e)FBM=ru@rh{D*kB{XxUg*eez0lzbVKqKdnma3SH}@C@y*rNGz$3RvE3-z8 zl0mX)l{Sf?w)Kz>OF@v|=j9O%-HOeRTwB%y#mrDC?eIYjc;OD8~nnS+Ss<8_uYcB6 z5O~ryv*%&X!<-&N;7MMWIEe`zsNMyW05Glb2QH=9Hv0Yi zaYrLi=N1K~jf&DVMaYg41yfaoI7*hqN%i4$aCWxR_w?!c=ijBiBp}-U(-UX>skX(L zn1jELlC-&pmrij}knWZwV(hoXc)W6*g7zG)>a`Z@9o$j)*L~pYo^ri|Ut8SIdBEe& z%Ig=bZ6v9QpHIy%MM!~3YHEZlPK#LthMpGtg7hiJV&znObVg^_f{f1gdD=!KCRf$F zoVdY4-y#mT<#JY;{+cIjt&DmV@5`v5oAWupJx5JZHAd2zjR}}HMpQAn4?`)w6(*%; zeDDvvaJAP_?H7WY^3{QOZj9t9lsN-tDa3-%Y6A%z1G96{77QIH8BB5Mo>qTX2cu`G2qQE@i-R8 zi~sN#p23gs0K7@!ggIrQv@J2sV|xL0@b}ZUHBrCVGsMjPc9aNt2hM?nUqd^?3O}HF8cl*13=tS;67DL{c diff --git a/source/classes/CustomSettings.py b/source/classes/CustomSettings.py index a86e782b..a9ef34d9 100644 --- a/source/classes/CustomSettings.py +++ b/source/classes/CustomSettings.py @@ -189,6 +189,14 @@ class CustomSettings(object): args.shuffle_sfx[p] = get_setting(settings['shuffle_sfx'], args.shuffle_sfx[p]) args.msu_resume[p] = get_setting(settings['msu_resume'], args.msu_resume[p]) + def has_setting(self, player, setting): + if 'settings' in self.file_source and player in self.file_source['settings']: + return setting in self.file_source['settings'][player] + return False + + def get_setting(self, player, setting): + return self.file_source['settings'][player][setting] + def get_item_pool(self): if 'item_pool' in self.file_source: return self.file_source['item_pool'] diff --git a/source/enemizer/Enemizer.py b/source/enemizer/Enemizer.py index 90c7ef34..178e24b1 100644 --- a/source/enemizer/Enemizer.py +++ b/source/enemizer/Enemizer.py @@ -1,4 +1,5 @@ import RaceRandom as random +from collections import defaultdict from Utils import snes_to_pc from source.dungeon.EnemyList import SpriteType, EnemySprite, sprite_translation @@ -428,12 +429,16 @@ def randomize_enemies(world, player): if world.enemy_shuffle[player] != 'none': data_tables = world.data_tables[player] custom_uw, custom_ow = {}, {} - enemy_map = world.customizer.get_enemies() if world.customizer else None - if enemy_map and player in enemy_map: - if 'Underworld' in enemy_map[player]: - custom_uw = enemy_map[player]['Underworld'] - if 'Overworld' in enemy_map[player]: - custom_ow = enemy_map[player]['Overworld'] + if world.force_enemy[player]: + custom_ow = {area_id: {i: world.force_enemy[player] for i, s in enumerate(sprite_list)} for area_id, sprite_list in world.data_tables[player].ow_enemy_table.items()} + custom_uw = {room_id: {i: world.force_enemy[player] for i, s in enumerate(sprite_list)} for room_id, sprite_list in world.data_tables[player].uw_enemy_table.room_map.items()} + else: + enemy_map = world.customizer.get_enemies() if world.customizer else None + if enemy_map and player in enemy_map: + if 'Underworld' in enemy_map[player]: + custom_uw = enemy_map[player]['Underworld'] + if 'Overworld' in enemy_map[player]: + custom_ow = enemy_map[player]['Overworld'] randomize_underworld_sprite_sheets(data_tables.sprite_sheets, data_tables, custom_uw) randomize_underworld_rooms(data_tables, world, player, custom_uw) randomize_overworld_sprite_sheets(data_tables.sprite_sheets, data_tables, custom_ow) diff --git a/source/enemizer/SpriteSheets.py b/source/enemizer/SpriteSheets.py index abc2387c..7de739f3 100644 --- a/source/enemizer/SpriteSheets.py +++ b/source/enemizer/SpriteSheets.py @@ -655,9 +655,11 @@ def setup_custom_enemy_sheets(custom_enemies, sheets, data_tables, sheet_range, if key not in requirements: continue req = requirements[key] - if isinstance(req, dict): + if isinstance(req, dict) and room_id in req: req = req[room_id] - if req.static or not req.can_randomize: + else: + req = None + if req and (req.static or not req.can_randomize): try: combine_req(sub_groups_choices, req) except IncompatibleEnemyException: