diff --git a/DoorShuffle.py b/DoorShuffle.py index 1b7e96b7..e64a3c19 100644 --- a/DoorShuffle.py +++ b/DoorShuffle.py @@ -3545,6 +3545,7 @@ class DROptions(Flag): Hide_Total = 0x100 DarkWorld_Spawns = 0x200 BigKeyDoor_Shuffle = 0x400 + EnemyDropIndicator = 0x800 # if on, enemy drop indicator show, else it doesn't # DATA GOES DOWN HERE diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 3231157f..391ec6a4 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -1,5 +1,37 @@ # New Features +FastROM changes have been included now. + +## Enemizer Features + +Please see this document for extensive details. [Enemizer in DR](https://docs.google.com/document/d/1iwY7Gy50DR3SsdXVaLFIbx4xRBqo9a-e1_jAl5LMCX8/edit?usp=sharing) + +Key points: +* Enemizer no longer uses a third party program. It is now built-in. +* New option under Shuffle Enemy Drops: Underworld. Any underworld enemy can drop items. +* New option under Enemizer tab: Enemy Logic + +Please read the entire document above for extensive details about enemizer and enemy drop shuffle systems. + +Enemizer main changes: +* Several sprites added to the pool. Most notable is how enemies behave on shallow water. They work now. +* Clearing rooms, spawnable chests, and enemy keys drops can now have enemies with specific logic in the room. This logic is controlled by the new Enemy Logic option +* New system for banning enemies that cause issue is place. If you see an enemy in a place that would cause issue, please report it and it can be banned to never happen again. Initial bans can be found [in the code](source/enemizer/enemy_deny.yaml) for the curious +* Thieves are always unkillable, but banned from the entire underworld. We can selectively ban them from problematic places in the overworld, and if someone wants to figure out where they could be safe in the underworld, I'll allow them there once the major problems have been banned. +* THe old "random" and "legacy" options have been discarded for enemy shuffle. Tile room patterns are currently shuffled with enemies. + +Underworld drops: + +* A flashing blue square added to help locate enemies that have remaining drops on the supertile. (Dungeons and caves without a compass get this for free.) +* Flying enemies, spawned enemies, and enemies with special death routines will not drop items. +* Pikits do not drop their item if they have eaten a shield. +* Hovers in swamp waterway do no drop items due to a layer issue that's not been solved. +* Enemies that are over pits require boomerang or hookshot to collect the item +* Enemies behind rails require the boomerang (hookshot can sequence break in certain cases) +* Enemies that spawn on walls do not drop items. (Keese normally don't, but in enemizer these can be valid drops otherwise. The document has a visual guide.) + +(Older notes below) + One major change with this update is that big key doors and certain trap doors are no longer guaranteed to be vanilla in Dungeon Door Shuffle modes even if you choose not to shuffle those types. A newer algorithm for putting dungeons together has been written and it will remove big key doors and trap doors when necessary to ensure progress can be made. Please note that retro features are now independently customizable as referenced below. Selecting Retro mode or World State: Retro will change Bow Mode to Retro (Progressive). Take Anys to Random, and Small Keys to Universal. diff --git a/Rom.py b/Rom.py index aa2f47c6..5ec5d5c0 100644 --- a/Rom.py +++ b/Rom.py @@ -40,7 +40,7 @@ from source.enemizer.Enemizer import write_enemy_shuffle_settings JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = 'f3cc032fa0a1ccf9624d0fb3e9926c3b' +RANDOMIZERBASEHASH = 'c8b9a36f11cb3cd070f83f31219f5cba' class JsonRom(object): @@ -573,6 +573,8 @@ def patch_rom(world, rom, player, team, is_mystery=False): dr_flags |= DROptions.Fix_EG if world.door_type_mode[player] in ['big', 'all', 'chaos']: dr_flags |= DROptions.BigKeyDoor_Shuffle + if world.dropshuffle[player] in ['underworld']: + dr_flags |= DROptions.EnemyDropIndicator my_locations = world.get_filled_locations(player) valid_locations = [l for l in my_locations if ((l.type == LocationType.Pot and not l.forced_item) diff --git a/data/base2current.bps b/data/base2current.bps index 947bf97a..a77da83b 100644 Binary files a/data/base2current.bps and b/data/base2current.bps differ diff --git a/resources/app/gui/randomize/enemizer/widgets.json b/resources/app/gui/randomize/enemizer/widgets.json index 0c78cb2d..cf03c320 100644 --- a/resources/app/gui/randomize/enemizer/widgets.json +++ b/resources/app/gui/randomize/enemizer/widgets.json @@ -4,9 +4,7 @@ "type": "selectbox", "options": [ "none", - "shuffled", - "random", - "legacy" + "shuffled" ] }, "bossshuffle": { diff --git a/source/dungeon/EnemyList.py b/source/dungeon/EnemyList.py index 0325fba3..b07c867e 100644 --- a/source/dungeon/EnemyList.py +++ b/source/dungeon/EnemyList.py @@ -468,7 +468,7 @@ def init_enemy_stats(): EnemySprite.Boulder: EnemyStats(EnemySprite.Boulder, True, dmg=4), EnemySprite.Gibo: EnemyStats(EnemySprite.Gibo, False, True, 0, health=8, dmg=3), # patrick! # could drop if killable thieves is on - EnemySprite.Thief: EnemyStats(EnemySprite.Thief, False, False, health=0, dmg=2), + EnemySprite.Thief: EnemyStats(EnemySprite.Thief, False, False, health=None, dmg=2), EnemySprite.Medusa: EnemyStats(EnemySprite.Medusa, True, ignore=True, dmg=0, dmask=0x10), EnemySprite.FourWayShooter: EnemyStats(EnemySprite.FourWayShooter, True, ignore=True, dmg=0), EnemySprite.Pokey: EnemyStats(EnemySprite.Pokey, False, True, 7, health=32, dmg=6), diff --git a/source/enemizer/Enemizer.py b/source/enemizer/Enemizer.py index 2460c7fa..93203acc 100644 --- a/source/enemizer/Enemizer.py +++ b/source/enemizer/Enemizer.py @@ -416,10 +416,10 @@ def randomize_enemies(world, player): randomize_overworld_sprite_sheets(data_tables.sprite_sheets, data_tables, custom_ow) randomize_overworld_enemies(data_tables, custom_ow) # fix thief stats - subclass_table = world.damage_table[player].damage_table['SubClassTable'] - subclass_table[EnemySprite.Thief] = subclass_table[EnemySprite.GreenEyegoreMimic] - data_tables.enemy_stats[EnemySprite.Thief].health = 4 - # todo: could turn droppable on here if we wanted for theives + # subclass_table = world.damage_table[player].damage_table['SubClassTable'] + # subclass_table[EnemySprite.Thief] = subclass_table[EnemySprite.GreenEyegoreMimic] + # data_tables.enemy_stats[EnemySprite.Thief].health = 4 + # could turn droppable on here if we wanted for killable theives # health shuffle if world.enemy_health[player] != 'default': stats = world.data_tables[player].enemy_stats @@ -475,8 +475,8 @@ def write_enemy_shuffle_settings(world, player, rom): rom.write_byte(snes_to_pc(0x368105), 0x01) # killable thief - rom.write_byte(snes_to_pc(0x368108), 0xc4) - rom.write_byte(snes_to_pc(0x0DB237), 4) # health value: # todo: thief health value + # rom.write_byte(snes_to_pc(0x368108), 0xc4) + # rom.write_byte(snes_to_pc(0x0DB237), 4) # health value - randomize it if killable, maybe # mimic room barriers data_tables = world.data_tables[player] diff --git a/source/enemizer/SpriteSheets.py b/source/enemizer/SpriteSheets.py index a1b681f3..f101ab0c 100644 --- a/source/enemizer/SpriteSheets.py +++ b/source/enemizer/SpriteSheets.py @@ -335,7 +335,7 @@ def init_sprite_requirements(): .sub_group(2, 0x42).sub_group(3, 0x43), SpriteRequirement(EnemySprite.Boulder).affix().sub_group(3, 0x10), SpriteRequirement(EnemySprite.Gibo).sub_group(2, 0x28), - SpriteRequirement(EnemySprite.Thief).no_drop().sub_group(0, [0xe, 0x15]), + SpriteRequirement(EnemySprite.Thief).immune().uw_skip().sub_group(0, [0xe, 0x15]), SpriteRequirement(EnemySprite.Medusa).affix(), SpriteRequirement(EnemySprite.FourWayShooter).affix(), SpriteRequirement(EnemySprite.Pokey).sub_group(2, 0x27), diff --git a/source/enemizer/enemy_deny.yaml b/source/enemizer/enemy_deny.yaml index 902629d0..a3c8e307 100644 --- a/source/enemizer/enemy_deny.yaml +++ b/source/enemizer/enemy_deny.yaml @@ -333,6 +333,8 @@ OwGeneralDeny: - [0x5e, 4, ["RollerVerticalUp", "Gibo"]] # forbid that one roller for kiki pod, and the kiki eating Gibo - [0x5e, 5, ["Gibo"]] # kiki eating Gibo UwEnemyDrop: + - [0x003d, 9, ["HardhatBeetle", "MiniHelmasaur", "Wizzrobe"]] # kept falling off before killing was possible + - [0x003d, 10, ["HardhatBeetle", "MiniHelmasaur", "Wizzrobe"]] # kept falling off before killing was possible - [0x0044, 4, ["HardhatBeetle"]] # kept falling off before killing was possible - [0x0085, 9, ["Babasu"]] # ran off the edge and didn't return - [0x00cc, 5, ["Babasu"]] # little hard to see and kill appropriately @@ -385,6 +387,18 @@ UwEnemyDrop: - [0x00c6, 6, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", "BombGuard", "GreenKnifeGuard"]] + # wizzrobe despawn issues - on pots/blocks + - [0x004e, 3, ["Wizzrobe"]] + - [0x005e, 4, ["Wizzrobe"]] + - [0x007e, 1, ["Wizzrobe"]] + - [0x007e, 6, ["Wizzrobe"]] + - [0x009f, 5, ["Wizzrobe"]] + - [0x00af, 0, ["Wizzrobe"]] + - [0x00bf, 1, ["Wizzrobe"]] + - [0x00ce, 5, ["Wizzrobe"]] + - [0x00ce, 6, ["Wizzrobe"]] + - [0x00ce, 7, ["Wizzrobe"]] + - [0x00ce, 8, ["Wizzrobe"]] # the following are all slightly in the wall on spawn - [0x0064, 0, ["Leever"]] - [0x00e5, 4, ["Leever"]]