Underworld dropshuffle

This commit is contained in:
aerinon
2022-09-30 15:38:35 -06:00
parent 3c0f6ca0e6
commit b71c7aa2b4
26 changed files with 182 additions and 136 deletions

View File

@@ -2132,6 +2132,7 @@ class Location(object):
self.real = not crystal
self.always_allow = lambda item, state: False
self.access_rule = lambda state: True
self.verbose_rule = None
self.item_rule = lambda item: True
self.player = player
self.skip = False
@@ -2601,7 +2602,7 @@ class Spoiler(object):
outfile.write(f"Decouple Doors: {yn(self.metadata['decoupledoors'][player])}\n")
outfile.write(f"Experimental: {yn(self.metadata['experimental'][player])}\n")
outfile.write(f"Dungeon Counters: {self.metadata['dungeon_counters'][player]}\n")
outfile.write(f"Drop Shuffle: {yn(self.metadata['dropshuffle'][player])}\n")
outfile.write(f"Drop Shuffle: {self.metadata['dropshuffle'][player]}\n")
outfile.write(f"Pottery Mode: {self.metadata['pottery'][player]}\n")
outfile.write(f"Pot Shuffle (Legacy): {yn(self.metadata['potshuffle'][player])}\n")
outfile.write('Map shuffle: %s\n' % ('Yes' if self.metadata['mapshuffle'][player] else 'No'))
@@ -2861,6 +2862,7 @@ mixed_travel_mode = {"prevent": 0, "allow": 1, "force": 2}
# new byte 4: TDDD PPPP (tavern shuffle, drop, pottery)
# dropshuffle reserves 2 bits, pottery needs 4)
drop_shuffle_mode = {'none': 0, 'keys': 1, 'underworld': 2}
pottery_mode = {'none': 0, 'keys': 2, 'lottery': 3, 'dungeon': 4, 'cave': 5, 'cavekeys': 6, 'reduced': 7,
'clustered': 8, 'nonempty': 9}
@@ -2919,7 +2921,7 @@ class Settings(object):
| (0x8 if w.standardize_palettes[p] == "original" else 0)
| (0 if w.intensity[p] == "random" else w.intensity[p]),
(0x80 if w.shuffletavern[p] else 0) | (0x10 if w.dropshuffle[p] else 0) | (pottery_mode[w.pottery[p]]),
(0x80 if w.shuffletavern[p] else 0) | (drop_shuffle_mode[w.dropshuffle[p]] << 4) | (pottery_mode[w.pottery[p]]),
((8 if w.crystals_gt_orig[p] == "random" else int(w.crystals_gt_orig[p])) << 3)
| (counter_mode[w.dungeon_counters[p]] << 1) | (1 if w.experimental[p] else 0),
@@ -2974,7 +2976,7 @@ class Settings(object):
args.intensity[p] = "random" if intensity == 0 else intensity
args.shuffletavern[p] = True if settings[4] & 0x80 else False
args.dropshuffle[p] = True if settings[4] & 0x10 else False
args.dropshuffle[p] = r(drop_shuffle_mode)[settings[4] & 0x70]
args.pottery[p] = r(pottery_mode)[settings[4] & 0x0F]
args.dungeon_counters[p] = r(counter_mode)[(settings[5] & 0x6) >> 1]

4
CLI.py
View File

@@ -107,7 +107,7 @@ def parse_cli(argv, no_defaults=False):
ret.keyshuffle = 'wild'
if ret.keydropshuffle:
ret.dropshuffle = True
ret.dropshuffle = 'keys' if ret.dropshuffle == 'none' else ret.dropshuffle
ret.pottery = 'keys' if ret.pottery == 'none' else ret.pottery
if ret.retro or ret.mode == 'retro':
@@ -196,7 +196,7 @@ def parse_settings():
"shopsanity": False,
'keydropshuffle': False,
'dropshuffle': False,
'dropshuffle': 'none',
'pottery': 'none',
'colorizepots': False,
'shufflepots': False,

View File

@@ -841,10 +841,10 @@ def main_dungeon_pool(dungeon_pool, world, player):
all_dungeon_items_cnt = len(list(y for x in world.dungeons if x.player == player for y in x.all_items))
target_items = 34
if world.keyshuffle[player] == 'universal':
target_items += 1 if world.dropshuffle[player] else 0 # the hc big key
target_items += 1 if world.dropshuffle[player] != 'none' else 0 # the hc big key
else:
target_items += 29 # small keys in chests
if world.dropshuffle[player]:
if world.dropshuffle[player] != 'none':
target_items += 14 # 13 dropped smalls + 1 big
if world.pottery[player] not in ['none', 'cave']:
target_items += 19 # 19 pot keys
@@ -1246,10 +1246,10 @@ def cross_dungeon(world, player):
all_dungeon_items_cnt = len(list(y for x in world.dungeons if x.player == player for y in x.all_items))
target_items = 34
if world.keyshuffle[player] == 'universal':
target_items += 1 if world.dropshuffle[player] else 0 # the hc big key
target_items += 1 if world.dropshuffle[player] != 'none' else 0 # the hc big key
else:
target_items += 29 # small keys in chests
if world.dropshuffle[player]:
if world.dropshuffle[player] != 'none':
target_items += 14 # 13 dropped smalls + 1 big
if world.pottery[player] not in ['none', 'cave']:
target_items += 19 # 19 pot keys
@@ -1335,7 +1335,7 @@ def assign_cross_keys(dungeon_builders, world, player):
start = time.process_time()
if world.keyshuffle[player] == 'universal':
remaining = 29
if world.dropshuffle[player]:
if world.dropshuffle[player] != 'none':
remaining += 13
if world.pottery[player] not in ['none', 'cave']:
remaining += 19

View File

@@ -414,7 +414,7 @@ def generate_itempool(world, player):
if world.take_any[player] != 'none':
set_up_take_anys(world, player, skip_pool_adjustments)
if world.keyshuffle[player] == 'universal':
if world.dropshuffle[player] and not skip_pool_adjustments:
if world.dropshuffle[player] != 'none' and not skip_pool_adjustments:
world.itempool += [ItemFactory('Small Key (Universal)', player)] * 13
if world.pottery[player] not in ['none', 'cave'] and not skip_pool_adjustments:
world.itempool += [ItemFactory('Small Key (Universal)', player)] * 19

View File

@@ -80,8 +80,8 @@ item_table = {'Bow': (True, False, None, 0x0B, 200, 'You have\nchosen the\narche
'Single Bomb': (False, False, None, 0x27, 5, 'I make things\ngo BOOM! But\njust once.', 'and the explosion', 'the bomb-holding kid', 'firecracker for sale', 'blend fungus into bomb', '\'splosion boy explodes again', 'a bomb'),
'Arrows (5)': (False, False, None, 0x5A, 15, 'This will give\nyou five shots\nwith your bow!', 'and the arrow pack', 'stick-collecting kid', 'sewing kit for sale', 'fungus for arrows', 'archer boy sews again', 'five arrows'),
'Small Magic': (False, False, None, 0x45, 5, 'A bit of magic', 'and the bit of magic', 'bit-o-magic kid', 'magic bit for sale', 'fungus for magic', 'magic boy conjures again', 'a bit of magic'),
'Big Magic': (False, False, None, 0x5A, 40, 'A lot of magic', 'and lots of magic', 'lot-o-magic kid', 'magic refill for sale', 'fungus for magic', 'magic boy conjures again', 'a magic refill'),
'Chicken': (False, False, None, 0x5A, 999, 'Cucco of Legend', 'and the legendary cucco', 'chicken kid', 'fried chicken for sale', 'fungus for chicken', 'cucco boy clucks again', 'a cucco'),
'Big Magic': (False, False, None, 0xB4, 40, 'A lot of magic', 'and lots of magic', 'lot-o-magic kid', 'magic refill for sale', 'fungus for magic', 'magic boy conjures again', 'a magic refill'),
'Chicken': (False, False, None, 0xB3, 999, 'Cucco of Legend', 'and the legendary cucco', 'chicken kid', 'fried chicken for sale', 'fungus for chicken', 'cucco boy clucks again', 'a cucco'),
'Bombs (3)': (False, False, None, 0x28, 15, 'I make things\ngo triple\nBOOM!!!', 'and the explosions', 'the bomb-holding kid', 'firecrackers for sale', 'blend fungus into bombs', '\'splosion boy explodes again', 'three bombs'),
'Bombs (10)': (False, False, None, 0x31, 50, 'I make things\ngo BOOM! Ten\ntimes!', 'and the explosions', 'the bomb-holding kid', 'firecrackers for sale', 'blend fungus into bombs', '\'splosion boy explodes again', 'ten bombs'),
'Bomb Upgrade (+10)': (False, False, None, 0x52, 100, 'Increase bomb\nstorage, low\nlow price', 'and the bomb bag', 'boom-enlarging kid', 'bomb boost for sale', 'the shroom goes boom', 'upgrade boy explodes more again', 'bomb capacity'),
@@ -174,7 +174,7 @@ item_table = {'Bow': (True, False, None, 0x0B, 200, 'You have\nchosen the\narche
'Blue Potion': (False, False, None, 0x30, 160, 'Delicious blue goop!', 'and the blue goo', 'the liquid kid', 'potion for sale', 'free samples', 'bottle boy has blue goo again', 'a blue potion'),
'Bee': (False, False, None, 0x0E, 10, 'I will sting your foes a few times', 'and the sting buddy', 'the beekeeper kid', 'insect for sale', 'shroom pollenation', 'bottle boy has mad bee again', 'a bee'),
'Small Heart': (False, False, None, 0x42, 10, 'Just a little\npiece of love!', 'and the heart', 'the life-giving kid', 'little love for sale', 'fungus for life', 'life boy feels some love again', 'a heart'),
'Fairy': (False, False, None, 0x5A, 50, 'Just a pixie!', 'and the pixie', 'the pixie kid', 'pixie for sale', 'pixie fungus', 'bottle boy has pixie again', 'a pixie'),
'Fairy': (False, False, None, 0xB2, 50, 'Just a pixie!', 'and the pixie', 'the pixie kid', 'pixie for sale', 'pixie fungus', 'bottle boy has pixie again', 'a pixie'),
'Beat Agahnim 1': (True, False, 'Event', 999, None, None, None, None, None, None, None, None),
'Beat Agahnim 2': (True, False, 'Event', 999, None, None, None, None, None, None, None, None),
'Get Frog': (True, False, 'Event', 999, None, None, None, None, None, None, None, None),

View File

@@ -1422,7 +1422,7 @@ def prize_relevance_sig2(start_regions, d_name, dungeon_entrance):
def validate_bk_layout(proposal, builder, start_regions, world, player):
bk_special = check_bk_special(builder.master_sector.regions, world, player)
if world.bigkeyshuffle[player] and (world.dropshuffle[player] or not bk_special):
if world.bigkeyshuffle[player] and (world.dropshuffle[player] != 'none' or not bk_special):
return True
flat_proposal = flatten_pair_list(proposal)
state = ExplorationState(dungeon=builder.name)

View File

@@ -461,11 +461,13 @@ def copy_world(world):
ret.bigkeyshuffle = world.bigkeyshuffle.copy()
ret.bombbag = world.bombbag.copy()
ret.flute_mode = world.flute_mode.copy()
ret.bow_mode = world.bow_mode.copy()
ret.crystals_needed_for_ganon = world.crystals_needed_for_ganon.copy()
ret.crystals_needed_for_gt = world.crystals_needed_for_gt.copy()
ret.crystals_ganon_orig = world.crystals_ganon_orig.copy()
ret.crystals_gt_orig = world.crystals_gt_orig.copy()
ret.open_pyramid = world.open_pyramid.copy()
ret.take_any = world.take_any.copy()
ret.boss_shuffle = world.boss_shuffle.copy()
ret.enemy_shuffle = world.enemy_shuffle.copy()
ret.enemy_health = world.enemy_health.copy()
@@ -481,6 +483,7 @@ def copy_world(world):
ret.mixed_travel = world.mixed_travel.copy()
ret.standardize_palettes = world.standardize_palettes.copy()
ret.restrict_boss_items = world.restrict_boss_items.copy()
ret.data_tables = world.data_tables # can be changed...
for player in range(1, world.players + 1):
if world.mode[player] != 'inverted':

View File

@@ -1023,8 +1023,8 @@ def adjust_locations(world, player):
loc.address = pot_address(pot_index, datum[1])
loc.pot = pot
pot.location = loc
if (not world.dropshuffle[player] and drop_location)\
or (not drop_location and world.pottery[player] in ['none', 'cave']):
if ((world.dropshuffle[player] == 'none' and drop_location)
or (not drop_location and world.pottery[player] in ['none', 'cave'])):
loc.skip = True
else:
key_item = loc.item

39
Rom.py
View File

@@ -33,11 +33,11 @@ from InitialSram import InitialSram
from source.classes.SFX import randomize_sfx
from source.item.FillUtil import valid_pot_items
from source.dungeon.RoomList import Room0127
from source.dungeon.EnemyList import EnemySprite
JAP10HASH = '03a63945398191337e896e5771f77173'
RANDOMIZERBASEHASH = '0587709ac8c5f2abf95b14d1e1264945'
RANDOMIZERBASEHASH = '81d7cf07a34d06ec875074296c39cd97'
class JsonRom(object):
@@ -616,18 +616,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
location.pot.indicator = standing_item_flag
location.pot.standing_item_code = code
continue
elif location.type == LocationType.Drop:
if location.item.player != player:
code = 0xF9
else:
code = 0xF8
sprite_pointer = snes_to_pc(location.address)
rom.write_byte(sprite_pointer, handle_native_dungeon(location, itemid))
if code == 0xF9:
rom.write_byte(sprite_pointer+1, location.item.player)
else:
rom.write_byte(sprite_pointer+1, 0)
rom.write_byte(sprite_pointer+2, code)
elif location.type == LocationType.Drop: # handled in the sprite table routine
continue
if location.address is None or (type(location.address) is int and location.address >= 0x400000):
continue
@@ -764,7 +753,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
valid_loc_by_dungeon = valid_dungeon_locations(valid_locations)
# fix hc big key problems (map and compass too)
if (world.doorShuffle[player] not in ['vanilla', 'basic'] or world.dropshuffle[player]
if (world.doorShuffle[player] not in ['vanilla', 'basic'] or world.dropshuffle[player] != 'none'
or world.pottery[player] not in ['none', 'cave']):
rom.write_byte(0x151f1, 2)
rom.write_byte(0x15270, 2)
@@ -894,9 +883,9 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
credits_total = len(valid_locations)
if world.dropshuffle[player] or world.pottery[player] != 'none':
if world.dropshuffle[player] != 'none' or world.pottery[player] != 'none':
rom.write_byte(0x142A50, 1) # StandingItemsOn
multiClientFlags = ((0x1 if world.dropshuffle[player] else 0)
multiClientFlags = ((0x1 if world.dropshuffle[player] != 'none' else 0)
| (0x2 if world.shopsanity[player] else 0)
| (0x4 if world.take_any[player] != 'none' else 0)
| (0x8 if world.pottery[player] != 'none' else 0)
@@ -904,7 +893,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
rom.write_byte(0x142A51, multiClientFlags)
# StandingItemCounterMask
rom.write_byte(0x142A55, ((0x1 if world.pottery[player] not in ['none', 'cave'] else 0)
| (0x2 if world.dropshuffle[player] else 0)))
| (0x2 if world.dropshuffle[player] != 'none' else 0)))
if world.pottery[player] not in ['none', 'keys']:
# Cuccos should not prevent kill rooms from opening
rom.write_byte(snes_to_pc(0x0DB457), 0x40)
@@ -1249,8 +1238,8 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
rom.write_bytes(0x180213, [0x00, 0x01]) # Not a Tournament Seed
gametype = 0x04 # item
if (world.shuffle[player] != 'vanilla' or world.doorShuffle[player] != 'vanilla' or world.dropshuffle[player]
or world.pottery[player] != 'none'):
if (world.shuffle[player] != 'vanilla' or world.doorShuffle[player] != 'vanilla'
or world.dropshuffle[player] != 'none' or world.pottery[player] != 'none'):
gametype |= 0x02 # entrance/door
if enemized:
gametype |= 0x01 # enemizer
@@ -1350,7 +1339,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
rom.write_byte(0x18003C, 0x00)
elif world.dungeon_counters[player] == 'on':
compass_mode = 0x02 # always on
elif (world.compassshuffle[player] or world.doorShuffle[player] != 'vanilla' or world.dropshuffle[player]
elif (world.compassshuffle[player] or world.doorShuffle[player] != 'vanilla' or world.dropshuffle[player] != 'none'
or world.dungeon_counters[player] == 'pickup' or world.pottery[player] not in ['none', 'cave']):
compass_mode = 0x01 # show on pickup
if world.shuffle[player] != 'vanilla' and world.overworld_map[player] != 'default':
@@ -1518,10 +1507,11 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
if item_dungeon == 'Escape':
item_dungeon = 'Hyrule Castle'
is_small_key_this_dungeon = hera_basement.parent_region.dungeon.name == item_dungeon
# hera small key is 11th in list, 10th sprite because of overlord
if is_small_key_this_dungeon:
rom.write_byte(0x4E3BB, 0xE4)
world.data_tables[player].uw_enemy_table.room_map[0x87][11].kind = EnemySprite.SmallKey
else:
rom.write_byte(0x4E3BB, 0xEB)
world.data_tables[player].uw_enemy_table.room_map[0x87][11].kind = EnemySprite.HeartPiece
# fix trock doors for reverse entrances
if world.fix_trock_doors[player]:
@@ -1533,7 +1523,8 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
rom.write_byte(0xFED31, 0x0E) # preopen bombable exit
rom.write_byte(0xFEE41, 0x0E) # preopen bombable exit
if (world.doorShuffle[player] != 'vanilla' or world.dropshuffle[player]
# todo: combine this with data_tables for in place edits
if (world.doorShuffle[player] != 'vanilla' or world.dropshuffle[player] != 'none'
or world.pottery[player] != 'none'):
for room in world.rooms:
if room.player == player and room.modified:

View File

@@ -9,6 +9,8 @@ from Dungeons import dungeon_table
from RoomData import DoorKind
from OverworldGlitchRules import overworld_glitches_rules
from source.dungeon.EnemyList import kill_rules
def set_rules(world, player):
@@ -35,6 +37,7 @@ def set_rules(world, player):
bomb_rules(world, player)
pot_rules(world, player)
drop_rules(world, player)
if world.logic[player] == 'noglitches':
no_glitches_rules(world, player)
@@ -770,6 +773,17 @@ def pot_rules(world, player):
add_rule(l, lambda state: state.can_hit_crystal(player))
def drop_rules(world, player):
data_tables = world.data_tables[player]
defeat_rules = kill_rules(world, player, data_tables.enemy_stats)
for super_tile, enemy_list in data_tables.uw_enemy_table.room_map.items():
for enemy in enemy_list:
if enemy.location:
# could handle odd health rules here? assume harder variant for now
verbose_rule = defeat_rules[enemy.kind]
enemy.location.verbose_rule = verbose_rule
add_rule(enemy.location, verbose_rule.rule_lambda)
def default_rules(world, player):
# overworld requirements

Binary file not shown.

View File

@@ -4,7 +4,7 @@ meta:
settings:
1:
door_shuffle: vanilla
dropshuffle: true
dropshuffle: keys
experimental: true
goal: ganon
hints: false

View File

@@ -6,7 +6,7 @@ meta:
settings:
1:
door_shuffle: basic
dropshuffle: true
dropshuffle: keys
experimental: true
goal: ganon
hints: true

View File

@@ -24,8 +24,9 @@
chaos: 1
decoupledoors: off
dropshuffle:
on: 1
off: 1
none: 4
keys: 1
underworld: 1
pottery:
none: 8
keys: 1

View File

@@ -14,8 +14,9 @@ intensity:
2: 1
3: 2 # intensity 3 usually yield more errors
dropshuffle:
on: 1
off: 1
none: 10 # fewer locations
keys: 1
underworld: 1
pottery:
none: 10 # fewer locations
keys: 1

View File

@@ -89,8 +89,11 @@
"type": "bool"
},
"dropshuffle" : {
"action": "store_true",
"type": "bool"
"choices" : [
"none",
"keys",
"underworld"
]
},
"pottery" : {
"choices" : [

View File

@@ -277,7 +277,11 @@
"keyshuffle": [ "Small Keys are no longer restricted to their dungeons, but can be anywhere. (default: %(default)s)" ],
"bigkeyshuffle": [ "Big Keys are no longer restricted to their dungeons, but can be anywhere. (default: %(default)s)" ],
"shopsanity": ["Shop contents are shuffle in the main item pool and other items can take their place. (default: %(default)s)"],
"dropshuffle": [ "Keys dropped by enemies are shuffled and other items can take their place. (default: %(default)s)"],
"dropshuffle": [ "Controls how enemies drop items (default: %(default)s)",
"None: Enemies drops prize packs or keys as normal",
"Keys: Enemies that drop keys can drop other items and the keys are shuffled into the pool",
"Underworld: All killable enemies in the underworld can drop items"
],
"pottery": [ "Controls how items under pots are shuffled and if other items can take their place:",
"None: No pots are changed",
"Keys: Key pots are included in the location pool and other items can take their place",

View File

@@ -59,7 +59,10 @@
"randomizer.dungeon.bigkeyshuffle": "Big Keys",
"randomizer.dungeon.keydropshuffle": "Key Drop Shuffle (Legacy)",
"randomizer.dungeon.decoupledoors": "Decouple Doors",
"randomizer.dungeon.dropshuffle": "Shuffle Enemy Key Drops",
"randomizer.dungeon.dropshuffle": "Shuffle Enemy Drops",
"randomizer.dungeon.none": "None",
"randomizer.dungeon.keys": "Small Key Enemies",
"randomizer.dungeon.underworld": "Underworld Enemies",
"randomizer.dungeon.potshuffle": "Pot Shuffle (Legacy)",
"randomizer.dungeon.pottery": "Pottery",
"randomizer.dungeon.pottery.none": "None",

View File

@@ -65,7 +65,14 @@
}
},
"colorizepots": { "type": "checkbox" },
"dropshuffle": { "type": "checkbox" },
"dropshuffle": {
"type": "selectbox",
"default": "none",
"options": [
"none",
"keys",
"underworld"
]},
"potshuffle": { "type": "checkbox" },
"experimental": { "type": "checkbox" },
"dungeon_counters": {

View File

@@ -82,7 +82,8 @@ class CustomSettings(object):
args.pottery[p] = get_setting(settings['pottery'], args.pottery[p])
if get_setting(settings['keydropshuffle'], args.keydropshuffle[p]):
args.dropshuffle[p] = True
if args.dropshuffle[p] == 'none':
args.dropshuffle[p] = 'keys'
if args.pottery[p] == 'none':
args.pottery[p] = 'keys'

View File

@@ -17,15 +17,27 @@ from source.logic.Rule import RuleFactory
class EnemyStats:
def __init__(self, sprite, static, drop_flag=False, prize_pack: typing.Union[tuple, int] = 0, sub_type=0):
def __init__(self, sprite, static, drop_flag=False, prize_pack: typing.Union[tuple, int] = 0,
sub_type=0, health=None):
self.sprite = sprite
self.sub_type = sub_type
self.static = static
# self.health = health
self.health = health
# self.damage = damage
self.drop_flag = drop_flag
self.prize_pack = prize_pack
# health special cases:
# Octorok light/dark world
# Hardhat Beetle - starting position
# Tektike - starting position?
# RatCricket light/dark world
# Keese light/dark world
# Rope light/dark world
# Raven light/dark world
class EnemySprite(FastEnum):
Raven = 0x00
@@ -252,34 +264,34 @@ def init_enemy_stats():
stats = {
EnemySprite.CorrectPullSwitch: EnemyStats(EnemySprite.CorrectPullSwitch, True),
EnemySprite.WrongPullSwitch: EnemyStats(EnemySprite.WrongPullSwitch, True),
EnemySprite.Octorok: EnemyStats(EnemySprite.Octorok, False, True, 2),
EnemySprite.Octorok: EnemyStats(EnemySprite.Octorok, False, True, 2, health=4),
EnemySprite.Moldorm: EnemyStats(EnemySprite.Moldorm, True, False),
EnemySprite.Cucco: EnemyStats(EnemySprite.Cucco, False, False),
EnemySprite.Octoballoon: EnemyStats(EnemySprite.Octoballoon, False, False, 0),
EnemySprite.Octoballoon: EnemyStats(EnemySprite.Octoballoon, False, False, 0, health=2),
EnemySprite.OctoballoonBaby: EnemyStats(EnemySprite.OctoballoonBaby, False),
EnemySprite.Hinox: EnemyStats(EnemySprite.Hinox, False, True, 4),
EnemySprite.Moblin: EnemyStats(EnemySprite.Moblin, False, True, 1),
EnemySprite.MiniHelmasaur: EnemyStats(EnemySprite.MiniHelmasaur, False, True, 7),
EnemySprite.Hinox: EnemyStats(EnemySprite.Hinox, False, True, 4, health=20),
EnemySprite.Moblin: EnemyStats(EnemySprite.Moblin, False, True, 1, health=4),
EnemySprite.MiniHelmasaur: EnemyStats(EnemySprite.MiniHelmasaur, False, True, 7, health=4),
EnemySprite.ThievesTownGrate: EnemyStats(EnemySprite.ThievesTownGrate, True),
EnemySprite.AntiFairy: EnemyStats(EnemySprite.AntiFairy, False, False),
EnemySprite.Wiseman: EnemyStats(EnemySprite.Wiseman, True),
EnemySprite.Hoarder: EnemyStats(EnemySprite.Hoarder, False, False),
EnemySprite.MiniMoldorm: EnemyStats(EnemySprite.MiniMoldorm, False, True, 2),
EnemySprite.Poe: EnemyStats(EnemySprite.Poe, False, True, 6),
EnemySprite.Hoarder: EnemyStats(EnemySprite.Hoarder, False, False, health=2),
EnemySprite.MiniMoldorm: EnemyStats(EnemySprite.MiniMoldorm, False, True, 2, health=3),
EnemySprite.Poe: EnemyStats(EnemySprite.Poe, False, True, 6, health=8),
EnemySprite.Smithy: EnemyStats(EnemySprite.Smithy, True),
EnemySprite.Arrow: EnemyStats(EnemySprite.Arrow, True),
EnemySprite.Statue: EnemyStats(EnemySprite.Statue, True),
EnemySprite.FluteQuest: EnemyStats(EnemySprite.FluteQuest, True),
EnemySprite.CrystalSwitch: EnemyStats(EnemySprite.CrystalSwitch, True),
EnemySprite.SickKid: EnemyStats(EnemySprite.SickKid, True),
EnemySprite.Sluggula: EnemyStats(EnemySprite.Sluggula, False, True, 4),
EnemySprite.Sluggula: EnemyStats(EnemySprite.Sluggula, False, True, 4, health=8),
EnemySprite.WaterSwitch: EnemyStats(EnemySprite.WaterSwitch, True),
EnemySprite.Ropa: EnemyStats(EnemySprite.Ropa, False, True, 2),
EnemySprite.RedBari: EnemyStats(EnemySprite.RedBari, False, True, 6, 2),
EnemySprite.BlueBari: EnemyStats(EnemySprite.BlueBari, False, True, 6, 2),
EnemySprite.Ropa: EnemyStats(EnemySprite.Ropa, False, True, 2, health=8),
EnemySprite.RedBari: EnemyStats(EnemySprite.RedBari, False, True, 6, 2, health=2),
EnemySprite.BlueBari: EnemyStats(EnemySprite.BlueBari, False, True, 6, 2, health=2),
EnemySprite.TalkingTree: EnemyStats(EnemySprite.TalkingTree, True),
EnemySprite.HardhatBeetle: EnemyStats(EnemySprite.HardhatBeetle, False, True, (2, 6)),
EnemySprite.HardhatBeetle: EnemyStats(EnemySprite.HardhatBeetle, False, True, (2, 6), health=32),
EnemySprite.Deadrock: EnemyStats(EnemySprite.Deadrock, False, False),
EnemySprite.DarkWorldHintNpc: EnemyStats(EnemySprite.DarkWorldHintNpc, True),
EnemySprite.AdultNpc: EnemyStats(EnemySprite.AdultNpc, True),
@@ -303,30 +315,30 @@ def init_enemy_stats():
EnemySprite.BonkItem: EnemyStats(EnemySprite.BonkItem, True),
EnemySprite.KidInKak: EnemyStats(EnemySprite.KidInKak, True),
EnemySprite.OldSnitch: EnemyStats(EnemySprite.OldSnitch, True),
EnemySprite.Hoarder2: EnemyStats(EnemySprite.Hoarder2, False, False),
EnemySprite.Hoarder2: EnemyStats(EnemySprite.Hoarder2, False, False, health=2),
EnemySprite.TutorialGuard: EnemyStats(EnemySprite.TutorialGuard, True),
EnemySprite.LightningGate: EnemyStats(EnemySprite.LightningGate, True),
EnemySprite.BlueGuard: EnemyStats(EnemySprite.BlueGuard, False, True, 1),
EnemySprite.GreenGuard: EnemyStats(EnemySprite.GreenGuard, False, True, 1),
EnemySprite.RedSpearGuard: EnemyStats(EnemySprite.RedSpearGuard, False, True, 1),
EnemySprite.BluesainBolt: EnemyStats(EnemySprite.BluesainBolt, False, True, 7),
EnemySprite.UsainBolt: EnemyStats(EnemySprite.UsainBolt, False, True, 1),
EnemySprite.BlueArcher: EnemyStats(EnemySprite.BlueArcher, False, True, 5),
EnemySprite.GreenBushGuard: EnemyStats(EnemySprite.GreenBushGuard, False, True, 5),
EnemySprite.RedJavelinGuard: EnemyStats(EnemySprite.RedJavelinGuard, False, True, 3),
EnemySprite.RedBushGuard: EnemyStats(EnemySprite.RedBushGuard, False, True, 7),
EnemySprite.BombGuard: EnemyStats(EnemySprite.BombGuard, False, True, 4),
EnemySprite.GreenKnifeGuard: EnemyStats(EnemySprite.GreenKnifeGuard, False, True, 1),
EnemySprite.Geldman: EnemyStats(EnemySprite.Geldman, False, True, 2),
EnemySprite.Popo: EnemyStats(EnemySprite.Popo, False, True, 2),
EnemySprite.Popo2: EnemyStats(EnemySprite.Popo2, False, True, 2),
EnemySprite.BlueGuard: EnemyStats(EnemySprite.BlueGuard, False, True, 1, health=6),
EnemySprite.GreenGuard: EnemyStats(EnemySprite.GreenGuard, False, True, 1, health=4),
EnemySprite.RedSpearGuard: EnemyStats(EnemySprite.RedSpearGuard, False, True, 1, health=8),
EnemySprite.BluesainBolt: EnemyStats(EnemySprite.BluesainBolt, False, True, 7, health=6),
EnemySprite.UsainBolt: EnemyStats(EnemySprite.UsainBolt, False, True, 1, health=8),
EnemySprite.BlueArcher: EnemyStats(EnemySprite.BlueArcher, False, True, 5, health=6),
EnemySprite.GreenBushGuard: EnemyStats(EnemySprite.GreenBushGuard, False, True, 5, health=4),
EnemySprite.RedJavelinGuard: EnemyStats(EnemySprite.RedJavelinGuard, False, True, 3, health=8),
EnemySprite.RedBushGuard: EnemyStats(EnemySprite.RedBushGuard, False, True, 7, health=8),
EnemySprite.BombGuard: EnemyStats(EnemySprite.BombGuard, False, True, 4, health=8),
EnemySprite.GreenKnifeGuard: EnemyStats(EnemySprite.GreenKnifeGuard, False, True, 1, health=4),
EnemySprite.Geldman: EnemyStats(EnemySprite.Geldman, False, True, 2, health=4),
EnemySprite.Popo: EnemyStats(EnemySprite.Popo, False, True, 2, health=2),
EnemySprite.Popo2: EnemyStats(EnemySprite.Popo2, False, True, 2, health=2),
EnemySprite.ArmosKnight: EnemyStats(EnemySprite.ArmosKnight, True, False),
EnemySprite.Lanmolas: EnemyStats(EnemySprite.Lanmolas, True, False),
EnemySprite.Zora: EnemyStats(EnemySprite.Zora, False, True, 4),
EnemySprite.DesertStatue: EnemyStats(EnemySprite.DesertStatue, True),
EnemySprite.Crab: EnemyStats(EnemySprite.Crab, False, True, 1),
EnemySprite.Crab: EnemyStats(EnemySprite.Crab, False, True, 1, health=2),
EnemySprite.LostWoodsBird: EnemyStats(EnemySprite.LostWoodsBird, True),
EnemySprite.LostWoodsSquirrel: EnemyStats(EnemySprite.LostWoodsSquirrel, True),
EnemySprite.SparkCW: EnemyStats(EnemySprite.SparkCW, False, False),
@@ -337,20 +349,20 @@ def init_enemy_stats():
EnemySprite.RollerHorizontalRight: EnemyStats(EnemySprite.RollerHorizontalRight, False, False),
EnemySprite.Beamos: EnemyStats(EnemySprite.Beamos, False, False),
EnemySprite.MasterSword: EnemyStats(EnemySprite.MasterSword, True),
EnemySprite.DebirandoPit: EnemyStats(EnemySprite.DebirandoPit, False, True, 2),
EnemySprite.Debirando: EnemyStats(EnemySprite.Debirando, False, True, 2),
EnemySprite.DebirandoPit: EnemyStats(EnemySprite.DebirandoPit, False, True, 2, health=4),
EnemySprite.Debirando: EnemyStats(EnemySprite.Debirando, False, True, 2, health=4),
EnemySprite.ArcheryNpc: EnemyStats(EnemySprite.ArcheryNpc, True),
EnemySprite.WallCannonVertLeft: EnemyStats(EnemySprite.WallCannonVertLeft, True),
EnemySprite.WallCannonVertRight: EnemyStats(EnemySprite.WallCannonVertRight, True),
EnemySprite.WallCannonHorzTop: EnemyStats(EnemySprite.WallCannonHorzTop, True),
EnemySprite.WallCannonHorzBottom: EnemyStats(EnemySprite.WallCannonHorzBottom, True),
EnemySprite.BallNChain: EnemyStats(EnemySprite.BallNChain, False, True, 2),
EnemySprite.CannonTrooper: EnemyStats(EnemySprite.CannonTrooper, False, True, 1),
EnemySprite.CricketRat: EnemyStats(EnemySprite.CricketRat, False, True, 2),
EnemySprite.Snake: EnemyStats(EnemySprite.Snake, False, True, (1, 7)),
EnemySprite.Keese: EnemyStats(EnemySprite.Keese, False, True, (0, 7)),
EnemySprite.BallNChain: EnemyStats(EnemySprite.BallNChain, False, True, 2, health=16),
EnemySprite.CannonTrooper: EnemyStats(EnemySprite.CannonTrooper, False, True, 1, health=3),
EnemySprite.CricketRat: EnemyStats(EnemySprite.CricketRat, False, True, 2, health=8),
EnemySprite.Snake: EnemyStats(EnemySprite.Snake, False, True, (1, 7), health=8),
EnemySprite.Keese: EnemyStats(EnemySprite.Keese, False, True, (0, 7), health=4),
EnemySprite.Leever: EnemyStats(EnemySprite.Leever, False, True, 1),
EnemySprite.Leever: EnemyStats(EnemySprite.Leever, False, True, 1, health=4),
EnemySprite.FairyPondTrigger: EnemyStats(EnemySprite.FairyPondTrigger, True),
EnemySprite.UnclePriest: EnemyStats(EnemySprite.UnclePriest, True),
EnemySprite.RunningNpc: EnemyStats(EnemySprite.RunningNpc, True),
@@ -358,26 +370,26 @@ def init_enemy_stats():
EnemySprite.Zelda: EnemyStats(EnemySprite.Zelda, True),
EnemySprite.Grandma: EnemyStats(EnemySprite.Grandma, True),
EnemySprite.Agahnim: EnemyStats(EnemySprite.Agahnim, True),
EnemySprite.FloatingSkull: EnemyStats(EnemySprite.FloatingSkull, False, True, 7),
EnemySprite.FloatingSkull: EnemyStats(EnemySprite.FloatingSkull, False, True, 7, health=24),
EnemySprite.BigSpike: EnemyStats(EnemySprite.BigSpike, False, False),
EnemySprite.FirebarCW: EnemyStats(EnemySprite.FirebarCW, False, False),
EnemySprite.FirebarCCW: EnemyStats(EnemySprite.FirebarCCW, False, False),
EnemySprite.Firesnake: EnemyStats(EnemySprite.Firesnake, False, False),
EnemySprite.Hover: EnemyStats(EnemySprite.Hover, False, True, 2),
EnemySprite.Hover: EnemyStats(EnemySprite.Hover, False, True, 2, health=4),
EnemySprite.AntiFairyCircle: EnemyStats(EnemySprite.AntiFairyCircle, False, False),
EnemySprite.GreenEyegoreMimic: EnemyStats(EnemySprite.GreenEyegoreMimic, False, True, 5),
EnemySprite.RedEyegoreMimic: EnemyStats(EnemySprite.RedEyegoreMimic, False, True, 5),
EnemySprite.YellowStalfos: EnemyStats(EnemySprite.YellowStalfos, True),
EnemySprite.Kondongo: EnemyStats(EnemySprite.Kondongo, False, True, 6),
EnemySprite.GreenEyegoreMimic: EnemyStats(EnemySprite.GreenEyegoreMimic, False, True, 5, health=16),
EnemySprite.RedEyegoreMimic: EnemyStats(EnemySprite.RedEyegoreMimic, False, True, 5, health=8),
EnemySprite.YellowStalfos: EnemyStats(EnemySprite.YellowStalfos, True, health=8),
EnemySprite.Kondongo: EnemyStats(EnemySprite.Kondongo, False, True, 6, health=1),
EnemySprite.Mothula: EnemyStats(EnemySprite.Mothula, True),
EnemySprite.SpikeBlock: EnemyStats(EnemySprite.SpikeBlock, False, False),
EnemySprite.Gibdo: EnemyStats(EnemySprite.Gibdo, False, True, 3),
EnemySprite.Gibdo: EnemyStats(EnemySprite.Gibdo, False, True, 3, health=32),
EnemySprite.Arrghus: EnemyStats(EnemySprite.Arrghus, True),
EnemySprite.Arrghi: EnemyStats(EnemySprite.Arrghi, True),
EnemySprite.Terrorpin: EnemyStats(EnemySprite.Terrorpin, False, True, 2),
EnemySprite.Blob: EnemyStats(EnemySprite.Blob, False, True, 1),
EnemySprite.Terrorpin: EnemyStats(EnemySprite.Terrorpin, False, True, 2, health=8),
EnemySprite.Blob: EnemyStats(EnemySprite.Blob, False, True, 1, health=4),
EnemySprite.Wallmaster: EnemyStats(EnemySprite.Wallmaster, True),
EnemySprite.StalfosKnight: EnemyStats(EnemySprite.StalfosKnight, False, True, 4),
EnemySprite.StalfosKnight: EnemyStats(EnemySprite.StalfosKnight, False, True, 4, health=64),
EnemySprite.HelmasaurKing: EnemyStats(EnemySprite.HelmasaurKing, True),
EnemySprite.Bumper: EnemyStats(EnemySprite.Bumper, True),
EnemySprite.Pirogusu: EnemyStats(EnemySprite.Pirogusu, True),
@@ -385,21 +397,21 @@ def init_enemy_stats():
EnemySprite.LaserEyeRight: EnemyStats(EnemySprite.LaserEyeRight, True),
EnemySprite.LaserEyeTop: EnemyStats(EnemySprite.LaserEyeTop, True),
EnemySprite.LaserEyeBottom: EnemyStats(EnemySprite.LaserEyeBottom, True),
EnemySprite.Pengator: EnemyStats(EnemySprite.Pengator, False, True, 3),
EnemySprite.Kyameron: EnemyStats(EnemySprite.Kyameron, False, False),
EnemySprite.Wizzrobe: EnemyStats(EnemySprite.Wizzrobe, False, True, 1),
EnemySprite.Zoro: EnemyStats(EnemySprite.Zoro, True),
EnemySprite.Babasu: EnemyStats(EnemySprite.Babasu, False, True, 0),
EnemySprite.Pengator: EnemyStats(EnemySprite.Pengator, False, True, 3, health=16),
EnemySprite.Kyameron: EnemyStats(EnemySprite.Kyameron, False, False, health=4),
EnemySprite.Wizzrobe: EnemyStats(EnemySprite.Wizzrobe, False, True, 1, health=2),
EnemySprite.Zoro: EnemyStats(EnemySprite.Zoro, True, health=4),
EnemySprite.Babasu: EnemyStats(EnemySprite.Babasu, False, True, 0, health=4),
EnemySprite.GroveOstritch: EnemyStats(EnemySprite.GroveOstritch, True),
EnemySprite.GroveRabbit: EnemyStats(EnemySprite.GroveRabbit, True),
EnemySprite.GroveBird: EnemyStats(EnemySprite.GroveBird, True),
EnemySprite.Freezor: EnemyStats(EnemySprite.Freezor, True, True, 0),
EnemySprite.Freezor: EnemyStats(EnemySprite.Freezor, True, True, 0, health=16),
EnemySprite.Kholdstare: EnemyStats(EnemySprite.Kholdstare, True),
EnemySprite.KholdstareShell: EnemyStats(EnemySprite.KholdstareShell, True),
EnemySprite.FallingIce: EnemyStats(EnemySprite.FallingIce, True),
EnemySprite.BlueZazak: EnemyStats(EnemySprite.BlueZazak, False, True, 6),
EnemySprite.RedZazak: EnemyStats(EnemySprite.RedZazak, False, True, 6),
EnemySprite.Stalfos: EnemyStats(EnemySprite.Stalfos, False, True, 6),
EnemySprite.BlueZazak: EnemyStats(EnemySprite.BlueZazak, False, True, 6, health=4),
EnemySprite.RedZazak: EnemyStats(EnemySprite.RedZazak, False, True, 6, health=8),
EnemySprite.Stalfos: EnemyStats(EnemySprite.Stalfos, False, True, 6, health=4),
# ... OW
EnemySprite.OldMan: EnemyStats(EnemySprite.OldMan, True),
EnemySprite.PipeDown: EnemyStats(EnemySprite.PipeDown, True),
@@ -418,23 +430,23 @@ def init_enemy_stats():
EnemySprite.Catfish: EnemyStats(EnemySprite.Catfish, True),
EnemySprite.CutsceneAgahnim: EnemyStats(EnemySprite.CutsceneAgahnim, True),
EnemySprite.Boulder: EnemyStats(EnemySprite.Boulder, True),
EnemySprite.Gibo: EnemyStats(EnemySprite.Gibo, False, True, 0), # patrick!
EnemySprite.Gibo: EnemyStats(EnemySprite.Gibo, False, True, 0, health=8), # patrick!
EnemySprite.Thief: EnemyStats(EnemySprite.Thief, False, False), # could drop if killable thieves is on
EnemySprite.Medusa: EnemyStats(EnemySprite.Medusa, True),
EnemySprite.FourWayShooter: EnemyStats(EnemySprite.FourWayShooter, True),
EnemySprite.Pokey: EnemyStats(EnemySprite.Pokey, False, True, 7),
EnemySprite.Pokey: EnemyStats(EnemySprite.Pokey, False, True, 7, health=32),
EnemySprite.BigFairy: EnemyStats(EnemySprite.BigFairy, True),
EnemySprite.Tektite: EnemyStats(EnemySprite.Tektite, False, True, 2),
EnemySprite.Tektite: EnemyStats(EnemySprite.Tektite, False, True, 2, health=12),
EnemySprite.Chainchomp: EnemyStats(EnemySprite.Chainchomp, False, False),
EnemySprite.TrinexxRockHead: EnemyStats(EnemySprite.TrinexxRockHead, True),
EnemySprite.TrinexxFireHead: EnemyStats(EnemySprite.TrinexxFireHead, True),
EnemySprite.TrinexxIceHead: EnemyStats(EnemySprite.TrinexxIceHead, True),
EnemySprite.Blind: EnemyStats(EnemySprite.Blind, True),
EnemySprite.Swamola: EnemyStats(EnemySprite.Swamola, False, True, 0),
EnemySprite.Lynel: EnemyStats(EnemySprite.Lynel, False, True, 7),
EnemySprite.Swamola: EnemyStats(EnemySprite.Swamola, False, True, 0, health=16),
EnemySprite.Lynel: EnemyStats(EnemySprite.Lynel, False, True, 7, health=24),
EnemySprite.BunnyBeam: EnemyStats(EnemySprite.BunnyBeam, False, False), # todo: medallions can kill bunny beams?
EnemySprite.FloppingFish: EnemyStats(EnemySprite.FloppingFish, True),
EnemySprite.Stal: EnemyStats(EnemySprite.Stal, False, True, 1),
EnemySprite.Stal: EnemyStats(EnemySprite.Stal, False, True, 1, health=4),
EnemySprite.Ganon: EnemyStats(EnemySprite.Ganon, True),
EnemySprite.Faerie: EnemyStats(EnemySprite.Faerie, True),
@@ -497,8 +509,6 @@ class Sprite(object):
# map of super_tile to list of Sprite objects:
vanilla_sprites = {}
enemy_stats = {}
def create_sprite(super_tile, kind, sub_type, layer, tile_x, tile_y, region=None,
drops_item=False, drop_item_kind=None):
@@ -1984,8 +1994,6 @@ def init_vanilla_sprites():
create_sprite(0x0126, EnemySprite.Faerie, 0x00, 0, 0x08, 0x16)
create_sprite(0x0126, EnemySprite.HeartPiece, 0x00, 0, 0x1c, 0x14)
create_sprite(0x0127, EnemySprite.HeartPiece, 0x00, 0, 0x07, 0x16)
global enemy_stats
enemy_stats = init_enemy_stats()
def kill_rules(world, player, stats):
@@ -2015,6 +2023,7 @@ def kill_rules(world, player, stats):
EnemySprite.GreenKnifeGuard: defeat_rule(world, player, h(EnemySprite.GreenKnifeGuard)),
EnemySprite.Popo: defeat_rule(world, player, h(EnemySprite.Popo), hook=True),
EnemySprite.Popo2: defeat_rule(world, player, h(EnemySprite.Popo2), hook=True),
EnemySprite.DebirandoPit: defeat_rule(world, player, h(EnemySprite.Debirando), fire=1, ice=1, boomerang=True),
EnemySprite.Debirando: defeat_rule(world, player, h(EnemySprite.Debirando), fire=1, ice=1, boomerang=True),
EnemySprite.BallNChain: defeat_rule(world, player, h(EnemySprite.BallNChain)),
EnemySprite.CannonTrooper: defeat_rule(world, player, h(EnemySprite.CannonTrooper), fire=1, ice=1),
@@ -2101,8 +2110,8 @@ def valid_drop_location(sprite, world, player):
if sprite.drops_item and sprite.drop_item_kind == 0xe4:
# already has a location
return False
else:
stat = enemy_stats[sprite.kind]
elif sprite.sub_type != SpriteType.Overlord:
stat = world.data_tables[player].enemy_stats[sprite.kind]
return not stat.static and stat.drop_flag
@@ -2120,6 +2129,7 @@ def create_drop_location(sprite, index, super_tile, world, player):
drop_location.drop = sprite
sprite.location = drop_location
drop_location.type = LocationType.Drop
parent.locations.append(drop_location)
# todo: placeholder address
@@ -2149,12 +2159,12 @@ prize_pack_selector = {
def add_drop_contents(world, player):
retro_bow = world.bow_mode[player].startswith('retro')
index_selector = [0]*8
for super_tile, enemy_list in world.enemy_list[player].room_map.items():
for super_tile, enemy_list in world.data_tables[player].uw_enemy_table.room_map.items():
for sprite in enemy_list:
if sprite.drops_item and sprite.drop_item_kind == 0xe4:
continue
else:
stat = enemy_stats[sprite.kind]
elif sprite.sub_type != SpriteType.Overlord:
stat = world.data_tables[player].enemy_stats[sprite.kind]
if not stat.static and stat.drop_flag:
pack = 0
if isinstance(stat.prize_pack, int):
@@ -2283,10 +2293,10 @@ def defeat_rule(world, player, health, class1=1,
else:
rules.append(can_fire_rod_kill(world, player, fire_rod_damage[fire], health))
if ice is not None:
rules.append(can_ice_rod_kill(world, player, ice_rod_damage[fire], health))
rules.append(can_ice_rod_kill(world, player, ice_rod_damage[ice], health))
if boomerang:
rules.append(has_boomerang(player))
return or_rule(rules)
return or_rule(*rules)
def has_blunt_weapon(player):
@@ -2298,7 +2308,7 @@ def find_shops_that_sell(item, world, player):
def can_shoot_arrows(world, player):
if world.retro[player]:
if world.bow_mode[player].startswith('retro'):
# todo: Non-progressive silvers grant wooden arrows, but progressive bows do not.
# Always require shop arrows to be safe
shops = find_shops_that_sell('Single Arrow', world, player)
@@ -2309,7 +2319,7 @@ def can_shoot_arrows(world, player):
def can_use_bombs(world, player):
return or_rule(RuleFactory.static_rule(not world.bombag[player]), has('Bomb Upgrade (+10)', player))
return or_rule(RuleFactory.static_rule(not world.bombbag[player]), has('Bomb Upgrade (+10)', player))
enemy_names = {
0x00: 'Raven',

View File

@@ -282,7 +282,7 @@ def create_guiargs(parent):
# Key drop shuffle stuff
if guiargs.keydropshuffle:
guiargs.dropshuffle = 1
guiargs.dropshuffle = 'keys' if guiargs.dropshuffle == 'none' else guiargs.dropshuffle
guiargs.pottery = 'keys' if guiargs.pottery == 'none' else guiargs.pottery
if guiargs.retro or guiargs.mode == 'retro':

View File

@@ -2,7 +2,6 @@ import RaceRandom as random
import logging
from collections import defaultdict
from source.dungeon.EnemyList import enemy_stats
from source.item.District import resolve_districts
from BaseClasses import PotItem, PotFlags
from DoorShuffle import validate_vanilla_reservation
@@ -66,7 +65,7 @@ def create_item_pool_config(world):
for player in range(1, world.players + 1):
config.static_placement[player] = defaultdict(list)
config.static_placement[player].update(vanilla_mapping)
if world.dropshuffle[player]:
if world.dropshuffle[player] != 'none':
for item, locs in keydrop_vanilla_mapping.items():
config.static_placement[player][item].extend(locs)
if world.pottery[player] not in ['none', 'cave']:
@@ -92,7 +91,7 @@ def create_item_pool_config(world):
for item, locs in vanilla_mapping.items():
if 'Small Key' in item:
universal_key_locations.extend(locs)
if world.dropshuffle[player]:
if world.dropshuffle[player] != 'none':
for item, locs in keydrop_vanilla_mapping.items():
if 'Small Key' in item:
universal_key_locations.extend(locs)

View File

@@ -108,6 +108,8 @@ class RuleFactory(object):
rule.sub_rules.extend(r.sub_rules) # todo: this extension for the lambda calc
elif r.rule_type == RuleType.Static and r.principal: # remove static flag if unnecessary
continue
elif r.rule_type == RuleType.Static and not r.principal: # always evaluates to false
return r
else:
rule.sub_rules.append(r)
if not rule_lambda:
@@ -128,6 +130,8 @@ class RuleFactory(object):
rule.sub_rules.extend(r.sub_rules) # todo: this extension for the lambda calc
elif r.rule_type == RuleType.Static and not r.principal: # remove static flag if unnecessary
continue
elif r.rule_type == RuleType.Static and r.principal: # always evaluates to true
return r
else:
rule.sub_rules.append(r)
if not rule_lambda:

View File

@@ -1,6 +1,6 @@
from Utils import snes_to_pc, int24_as_bytes, int16_as_bytes
from source.dungeon.EnemyList import EnemyTable, init_vanilla_sprites, vanilla_sprites
from source.dungeon.EnemyList import EnemyTable, init_vanilla_sprites, vanilla_sprites, init_enemy_stats
from source.dungeon.RoomHeader import init_room_headers
from source.dungeon.RoomList import Room0127
from source.enemizer.SpriteSheets import init_sprite_sheets, init_sprite_requirements
@@ -17,6 +17,7 @@ class DataTables:
# associated data
self.sprite_requirements = None
self.enemy_stats = None
def write_to_rom(self, rom, colorize_pots=False):
if self.pot_secret_table.size() > 0x11c0:
@@ -56,6 +57,7 @@ def init_data_tables(world, player):
data_tables.sprite_requirements = init_sprite_requirements()
data_tables.sprite_sheets = init_sprite_sheets(data_tables.sprite_requirements)
init_vanilla_sprites()
data_tables.enemy_stats = init_enemy_stats()
uw_table = data_tables.uw_enemy_table = EnemyTable()
for room, sprite_list in vanilla_sprites.items():
for sprite in sprite_list:

View File

@@ -97,7 +97,8 @@ def roll_settings(weights):
ret.pseudoboots = get_choice('pseudoboots') == 'on'
ret.shopsanity = get_choice('shopsanity') == 'on'
keydropshuffle = get_choice('keydropshuffle') == 'on'
ret.dropshuffle = get_choice('dropshuffle') == 'on' or keydropshuffle
ret.dropshuffle = get_choice('dropshuffle') if 'dropshuffle' in weights else 'none'
ret.dropshuffle = 'keys' if ret.dropshuffle == 'none' and keydropshuffle else ret.dropshuffle
ret.pottery = get_choice('pottery') if 'pottery' in weights else 'none'
ret.pottery = 'keys' if ret.pottery == 'none' and keydropshuffle else ret.pottery
ret.colorizepots = get_choice('colorizepots') == 'on'