any_enemy_logic option added
This commit is contained in:
@@ -128,6 +128,7 @@ class World(object):
|
|||||||
set_player_attr('enemy_shuffle', 'none')
|
set_player_attr('enemy_shuffle', 'none')
|
||||||
set_player_attr('enemy_health', 'default')
|
set_player_attr('enemy_health', 'default')
|
||||||
set_player_attr('enemy_damage', 'default')
|
set_player_attr('enemy_damage', 'default')
|
||||||
|
set_player_attr('any_enemy_logic', 'allow_all')
|
||||||
set_player_attr('beemizer', 0)
|
set_player_attr('beemizer', 0)
|
||||||
set_player_attr('escape_assist', [])
|
set_player_attr('escape_assist', [])
|
||||||
set_player_attr('crystals_needed_for_ganon', 7)
|
set_player_attr('crystals_needed_for_ganon', 7)
|
||||||
@@ -2497,6 +2498,7 @@ class Spoiler(object):
|
|||||||
'enemy_shuffle': self.world.enemy_shuffle,
|
'enemy_shuffle': self.world.enemy_shuffle,
|
||||||
'enemy_health': self.world.enemy_health,
|
'enemy_health': self.world.enemy_health,
|
||||||
'enemy_damage': self.world.enemy_damage,
|
'enemy_damage': self.world.enemy_damage,
|
||||||
|
'any_enemy_logic': self.world.any_enemy_logic,
|
||||||
'players': self.world.players,
|
'players': self.world.players,
|
||||||
'teams': self.world.teams,
|
'teams': self.world.teams,
|
||||||
'experimental': self.world.experimental,
|
'experimental': self.world.experimental,
|
||||||
@@ -2701,6 +2703,8 @@ class Spoiler(object):
|
|||||||
outfile.write('Enemy shuffle: %s\n' % self.metadata['enemy_shuffle'][player])
|
outfile.write('Enemy shuffle: %s\n' % self.metadata['enemy_shuffle'][player])
|
||||||
outfile.write('Enemy health: %s\n' % self.metadata['enemy_health'][player])
|
outfile.write('Enemy health: %s\n' % self.metadata['enemy_health'][player])
|
||||||
outfile.write('Enemy damage: %s\n' % self.metadata['enemy_damage'][player])
|
outfile.write('Enemy damage: %s\n' % self.metadata['enemy_damage'][player])
|
||||||
|
if self.metadata['enemy_shuffle'][player] != 'none':
|
||||||
|
outfile.write(f"Enemy logic: {self.metadata['any_enemy_logic'][player]}\n")
|
||||||
outfile.write(f"Hints: {yn(self.metadata['hints'][player])}\n")
|
outfile.write(f"Hints: {yn(self.metadata['hints'][player])}\n")
|
||||||
outfile.write('Race: %s\n' % ('Yes' if self.world.settings.world_rep['meta']['race'] else 'No'))
|
outfile.write('Race: %s\n' % ('Yes' if self.world.settings.world_rep['meta']['race'] else 'No'))
|
||||||
|
|
||||||
|
|||||||
3
CLI.py
3
CLI.py
@@ -141,7 +141,7 @@ def parse_cli(argv, no_defaults=False):
|
|||||||
'heartbeep', 'remote_items', 'shopsanity', 'dropshuffle', 'pottery', 'keydropshuffle',
|
'heartbeep', 'remote_items', 'shopsanity', 'dropshuffle', 'pottery', 'keydropshuffle',
|
||||||
'mixed_travel', 'standardize_palettes', 'code', 'reduce_flashing', 'shuffle_sfx',
|
'mixed_travel', 'standardize_palettes', 'code', 'reduce_flashing', 'shuffle_sfx',
|
||||||
'msu_resume', 'collection_rate', 'colorizepots', 'decoupledoors', 'door_type_mode',
|
'msu_resume', 'collection_rate', 'colorizepots', 'decoupledoors', 'door_type_mode',
|
||||||
'trap_door_mode', 'key_logic_algorithm']:
|
'trap_door_mode', 'key_logic_algorithm', 'any_enemy_logic']:
|
||||||
value = getattr(defaults, name) if getattr(playerargs, name) is None else getattr(playerargs, name)
|
value = getattr(defaults, name) if getattr(playerargs, name) is None else getattr(playerargs, name)
|
||||||
if player == 1:
|
if player == 1:
|
||||||
setattr(ret, name, {1: value})
|
setattr(ret, name, {1: value})
|
||||||
@@ -199,6 +199,7 @@ def parse_settings():
|
|||||||
"shufflebosses": "none",
|
"shufflebosses": "none",
|
||||||
"enemy_damage": "default",
|
"enemy_damage": "default",
|
||||||
"enemy_health": "default",
|
"enemy_health": "default",
|
||||||
|
'any_enemy_logic': 'allow_all',
|
||||||
|
|
||||||
"shopsanity": False,
|
"shopsanity": False,
|
||||||
'keydropshuffle': False,
|
'keydropshuffle': False,
|
||||||
|
|||||||
2
Main.py
2
Main.py
@@ -112,6 +112,7 @@ def main(args, seed=None, fish=None):
|
|||||||
world.enemy_shuffle = args.shuffleenemies.copy()
|
world.enemy_shuffle = args.shuffleenemies.copy()
|
||||||
world.enemy_health = args.enemy_health.copy()
|
world.enemy_health = args.enemy_health.copy()
|
||||||
world.enemy_damage = args.enemy_damage.copy()
|
world.enemy_damage = args.enemy_damage.copy()
|
||||||
|
world.any_enemy_logic = args.any_enemy_logic.copy()
|
||||||
world.beemizer = args.beemizer.copy()
|
world.beemizer = args.beemizer.copy()
|
||||||
world.intensity = {player: random.randint(1, 3) if args.intensity[player] == 'random' else int(args.intensity[player]) for player in range(1, world.players + 1)}
|
world.intensity = {player: random.randint(1, 3) if args.intensity[player] == 'random' else int(args.intensity[player]) for player in range(1, world.players + 1)}
|
||||||
world.door_type_mode = args.door_type_mode.copy()
|
world.door_type_mode = args.door_type_mode.copy()
|
||||||
@@ -454,6 +455,7 @@ def copy_world(world):
|
|||||||
ret.enemy_shuffle = world.enemy_shuffle.copy()
|
ret.enemy_shuffle = world.enemy_shuffle.copy()
|
||||||
ret.enemy_health = world.enemy_health.copy()
|
ret.enemy_health = world.enemy_health.copy()
|
||||||
ret.enemy_damage = world.enemy_damage.copy()
|
ret.enemy_damage = world.enemy_damage.copy()
|
||||||
|
ret.any_enemy_logic = world.any_enemy_logic.copy()
|
||||||
ret.beemizer = world.beemizer.copy()
|
ret.beemizer = world.beemizer.copy()
|
||||||
ret.intensity = world.intensity.copy()
|
ret.intensity = world.intensity.copy()
|
||||||
ret.decoupledoors = world.decoupledoors.copy()
|
ret.decoupledoors = world.decoupledoors.copy()
|
||||||
|
|||||||
@@ -162,6 +162,10 @@
|
|||||||
none: 3
|
none: 3
|
||||||
shuffled: 2
|
shuffled: 2
|
||||||
random: 1
|
random: 1
|
||||||
|
any_enemy_logic:
|
||||||
|
none: 1
|
||||||
|
allow_drops: 2
|
||||||
|
allow_all: 3
|
||||||
hints:
|
hints:
|
||||||
on: 1
|
on: 1
|
||||||
off: 1
|
off: 1
|
||||||
|
|||||||
@@ -146,6 +146,7 @@ enemy_shuffle: # shouldn't affect generation
|
|||||||
shuffled: 1
|
shuffled: 1
|
||||||
random: 1
|
random: 1
|
||||||
legacy: 0
|
legacy: 0
|
||||||
|
any_enemy_logic: allow_all # more interesting logic
|
||||||
hints:
|
hints:
|
||||||
on: 1
|
on: 1
|
||||||
off: 1
|
off: 1
|
||||||
|
|||||||
@@ -494,6 +494,13 @@
|
|||||||
"random"
|
"random"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"any_enemy_logic": {
|
||||||
|
"choices": [
|
||||||
|
"none",
|
||||||
|
"allow_drops",
|
||||||
|
"allow_all"
|
||||||
|
]
|
||||||
|
},
|
||||||
"remote_items": {
|
"remote_items": {
|
||||||
"action": "store_true",
|
"action": "store_true",
|
||||||
"type": "bool"
|
"type": "bool"
|
||||||
|
|||||||
@@ -335,6 +335,12 @@
|
|||||||
],
|
],
|
||||||
"pseudoboots": [ " Players starts with pseudo boots that allow dashing but no item checks (default: %(default)s"],
|
"pseudoboots": [ " Players starts with pseudo boots that allow dashing but no item checks (default: %(default)s"],
|
||||||
"bombbag": ["Start with 0 bomb capacity. Two capacity upgrades (+10) are added to the pool (default: %(default)s)" ],
|
"bombbag": ["Start with 0 bomb capacity. Two capacity upgrades (+10) are added to the pool (default: %(default)s)" ],
|
||||||
|
"any_enemy_logic": [
|
||||||
|
"How to handle potential traversal between dungeon in Crossed door shuffle",
|
||||||
|
"None: Enemies that required unusual weaponry are not allowed to logical protect doors, chests, or key drops.",
|
||||||
|
"Allow_Drops: Drops are okay for those enemies, and a correct weapon is logically required",
|
||||||
|
"Allow_All: All enemies allowed anywhere. (Logic will be adapted to the placement)"
|
||||||
|
],
|
||||||
"startinventory": [ "Specifies a list of items that will be in your starting inventory (separated by commas). (default: %(default)s)" ],
|
"startinventory": [ "Specifies a list of items that will be in your starting inventory (separated by commas). (default: %(default)s)" ],
|
||||||
"usestartinventory": [ "Toggle usage of Starting Inventory." ],
|
"usestartinventory": [ "Toggle usage of Starting Inventory." ],
|
||||||
"customizer": ["Path to a customizer file."],
|
"customizer": ["Path to a customizer file."],
|
||||||
|
|||||||
@@ -130,6 +130,11 @@
|
|||||||
"randomizer.enemizer.enemyhealth.hard": "Hard",
|
"randomizer.enemizer.enemyhealth.hard": "Hard",
|
||||||
"randomizer.enemizer.enemyhealth.expert": "Expert",
|
"randomizer.enemizer.enemyhealth.expert": "Expert",
|
||||||
|
|
||||||
|
"randomizer.enemizer.enemylogic": "Enemy Logic",
|
||||||
|
"randomizer.enemizer.enemylogic.none": "Forbid special enemies",
|
||||||
|
"randomizer.enemizer.enemylogic.allow_drops": "Item drops may have special enemies",
|
||||||
|
"randomizer.enemizer.enemylogic.allow_all": "Allow special enemies anywhere",
|
||||||
|
|
||||||
"randomizer.entrance.openpyramid": "Pre-open Pyramid Hole",
|
"randomizer.entrance.openpyramid": "Pre-open Pyramid Hole",
|
||||||
"randomizer.entrance.openpyramid.auto": "Auto",
|
"randomizer.entrance.openpyramid.auto": "Auto",
|
||||||
"randomizer.entrance.openpyramid.yes": "Yes",
|
"randomizer.entrance.openpyramid.yes": "Yes",
|
||||||
|
|||||||
@@ -39,5 +39,19 @@
|
|||||||
"expert"
|
"expert"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"bottomEnemizerFrame": {
|
||||||
|
"enemylogic": {
|
||||||
|
"type": "selectbox",
|
||||||
|
"default": "allow_all",
|
||||||
|
"options": [
|
||||||
|
"none",
|
||||||
|
"allow_drops",
|
||||||
|
"allow_all"
|
||||||
|
],
|
||||||
|
"config": {
|
||||||
|
"width": 32
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -134,6 +134,7 @@ class CustomSettings(object):
|
|||||||
args.shuffleenemies[p] = get_setting(settings['enemy_shuffle'], args.shuffleenemies[p])
|
args.shuffleenemies[p] = get_setting(settings['enemy_shuffle'], args.shuffleenemies[p])
|
||||||
args.enemy_health[p] = get_setting(settings['enemy_health'], args.enemy_health[p])
|
args.enemy_health[p] = get_setting(settings['enemy_health'], args.enemy_health[p])
|
||||||
args.enemy_damage[p] = get_setting(settings['enemy_damage'], args.enemy_damage[p])
|
args.enemy_damage[p] = get_setting(settings['enemy_damage'], args.enemy_damage[p])
|
||||||
|
args.any_enemy_logic[p] = get_setting(settings['any_enemy_logic'], args.any_enemy_logic[p])
|
||||||
args.shufflepots[p] = get_setting(settings['shufflepots'], args.shufflepots[p])
|
args.shufflepots[p] = get_setting(settings['shufflepots'], args.shufflepots[p])
|
||||||
args.bombbag[p] = get_setting(settings['bombbag'], args.bombbag[p])
|
args.bombbag[p] = get_setting(settings['bombbag'], args.bombbag[p])
|
||||||
args.shufflelinks[p] = get_setting(settings['shufflelinks'], args.shufflelinks[p])
|
args.shufflelinks[p] = get_setting(settings['shufflelinks'], args.shufflelinks[p])
|
||||||
@@ -254,6 +255,7 @@ class CustomSettings(object):
|
|||||||
settings_dict[p]['shuffleenemies'] = world.enemy_shuffle[p]
|
settings_dict[p]['shuffleenemies'] = world.enemy_shuffle[p]
|
||||||
settings_dict[p]['enemy_health'] = world.enemy_health[p]
|
settings_dict[p]['enemy_health'] = world.enemy_health[p]
|
||||||
settings_dict[p]['enemy_damage'] = world.enemy_damage[p]
|
settings_dict[p]['enemy_damage'] = world.enemy_damage[p]
|
||||||
|
settings_dict[p]['any_enemy_logic'] = world.any_enemy_logic[p]
|
||||||
settings_dict[p]['shufflepots'] = world.potshuffle[p]
|
settings_dict[p]['shufflepots'] = world.potshuffle[p]
|
||||||
settings_dict[p]['bombbag'] = world.bombbag[p]
|
settings_dict[p]['bombbag'] = world.bombbag[p]
|
||||||
settings_dict[p]['shufflelinks'] = world.shufflelinks[p]
|
settings_dict[p]['shufflelinks'] = world.shufflelinks[p]
|
||||||
|
|||||||
@@ -114,7 +114,8 @@ SETTINGSTOPROCESS = {
|
|||||||
"enemyshuffle": "shuffleenemies",
|
"enemyshuffle": "shuffleenemies",
|
||||||
"bossshuffle": "shufflebosses",
|
"bossshuffle": "shufflebosses",
|
||||||
"enemydamage": "enemy_damage",
|
"enemydamage": "enemy_damage",
|
||||||
"enemyhealth": "enemy_health"
|
"enemyhealth": "enemy_health",
|
||||||
|
"enemylogic": "any_enemy_logic"
|
||||||
},
|
},
|
||||||
"gameoptions": {
|
"gameoptions": {
|
||||||
"nobgm": "disablemusic",
|
"nobgm": "disablemusic",
|
||||||
|
|||||||
@@ -289,16 +289,12 @@ def randomize_underworld_rooms(data_tables, world, player):
|
|||||||
if wallmaster_chosen:
|
if wallmaster_chosen:
|
||||||
candidate_sprites = [x for x in candidate_sprites if x.sprite != EnemySprite.Wallmaster]
|
candidate_sprites = [x for x in candidate_sprites if x.sprite != EnemySprite.Wallmaster]
|
||||||
if sprite.drops_item:
|
if sprite.drops_item:
|
||||||
choice_list = [x for x in candidate_sprites if x.good_for_key_drop()]
|
forbidden = determine_forbidden(any_enemy_logic == 'none', room_id, True)
|
||||||
|
choice_list = [x for x in candidate_sprites if x.good_for_key_drop(forbidden)]
|
||||||
# terrorpin, deadrock, buzzblob, lynel, redmimic/eyegore
|
# terrorpin, deadrock, buzzblob, lynel, redmimic/eyegore
|
||||||
elif room_id in shutter_sprites and i in shutter_sprites[room_id]:
|
elif room_id in shutter_sprites and i in shutter_sprites[room_id]:
|
||||||
forbidden_set = set()
|
forbidden = determine_forbidden(any_enemy_logic != 'allow_all', room_id)
|
||||||
if not any_enemy_logic:
|
choice_list = [x for x in candidate_sprites if x.good_for_shutter(forbidden)]
|
||||||
forbidden_set.update({EnemySprite.Terrorpin, EnemySprite.Deadrock, EnemySprite.Buzzblob,
|
|
||||||
EnemySprite.Lynel})
|
|
||||||
if room_id not in {0x6b, 0x4b, 0x1b, 0xd8}: # mimics/eyegore are allowed in vanilla
|
|
||||||
forbidden_set.add(EnemySprite.RedEyegoreMimic)
|
|
||||||
choice_list = [x for x in candidate_sprites if x.good_for_shutter(forbidden_set)]
|
|
||||||
else:
|
else:
|
||||||
choice_list = [x for x in candidate_sprites if not x.water_only]
|
choice_list = [x for x in candidate_sprites if not x.water_only]
|
||||||
choice_list = filter_choices(choice_list, room_id, i, data_tables.uw_enemy_denials)
|
choice_list = filter_choices(choice_list, room_id, i, data_tables.uw_enemy_denials)
|
||||||
@@ -317,6 +313,20 @@ def randomize_underworld_rooms(data_tables, world, player):
|
|||||||
# done with rooms
|
# done with rooms
|
||||||
|
|
||||||
|
|
||||||
|
def determine_forbidden(forbid, room_id, drop_flag=False):
|
||||||
|
forbidden_set = set()
|
||||||
|
if forbid:
|
||||||
|
forbidden_set.update({EnemySprite.Terrorpin, EnemySprite.Deadrock, EnemySprite.Buzzblob,
|
||||||
|
EnemySprite.Lynel})
|
||||||
|
if drop_flag:
|
||||||
|
forbidden_set.add(EnemySprite.RedBari) # requires FireRod to Drop
|
||||||
|
# else: Not yet able to protect triggers, would change default GT tile room behavior
|
||||||
|
# forbidden_set.add(EnemySprite.AntiFairy) # can't drop anyway
|
||||||
|
if room_id not in {0x6b, 0x4b, 0x1b, 0xd8}: # mimics/eyegore are allowed in vanilla rooms
|
||||||
|
forbidden_set.add(EnemySprite.RedEyegoreMimic)
|
||||||
|
return forbidden_set
|
||||||
|
|
||||||
|
|
||||||
def filter_choices(options, room_id, sprite_idx, denials):
|
def filter_choices(options, room_id, sprite_idx, denials):
|
||||||
key = room_id, sprite_idx
|
key = room_id, sprite_idx
|
||||||
return [x for x in options if key not in denials or x.sprite not in denials[key]]
|
return [x for x in options if key not in denials or x.sprite not in denials[key]]
|
||||||
|
|||||||
@@ -96,8 +96,8 @@ class SpriteRequirement:
|
|||||||
return False
|
return False
|
||||||
return self.killable and not self.static and not self.dont_use and self.uw_valid
|
return self.killable and not self.static and not self.dont_use and self.uw_valid
|
||||||
|
|
||||||
def good_for_key_drop(self):
|
def good_for_key_drop(self, forbidden):
|
||||||
return self.good_for_shutter() and self.can_drop
|
return self.good_for_shutter(forbidden) and self.can_drop
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f'Req for {enemy_names[self.sprite]}'
|
return f'Req for {enemy_names[self.sprite]}'
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ def enemizer_page(parent,settings):
|
|||||||
self.frames["selectOptionsFrame"].pack(fill=X)
|
self.frames["selectOptionsFrame"].pack(fill=X)
|
||||||
self.frames["leftEnemizerFrame"].pack(side=LEFT)
|
self.frames["leftEnemizerFrame"].pack(side=LEFT)
|
||||||
self.frames["rightEnemizerFrame"].pack(side=RIGHT)
|
self.frames["rightEnemizerFrame"].pack(side=RIGHT)
|
||||||
self.frames["bottomEnemizerFrame"].pack(fill=X)
|
self.frames["bottomEnemizerFrame"].pack(fill=X, padx=(12, 0))
|
||||||
|
|
||||||
# Load Enemizer option widgets as defined by JSON file
|
# Load Enemizer option widgets as defined by JSON file
|
||||||
# Defns include frame name, widget type, widget options, widget placement attributes
|
# Defns include frame name, widget type, widget options, widget placement attributes
|
||||||
@@ -40,6 +40,8 @@ def enemizer_page(parent,settings):
|
|||||||
packAttrs = {"anchor":E}
|
packAttrs = {"anchor":E}
|
||||||
if self.widgets[key].type == "checkbox":
|
if self.widgets[key].type == "checkbox":
|
||||||
packAttrs["anchor"] = W
|
packAttrs["anchor"] = W
|
||||||
|
if framename == 'bottomEnemizerFrame':
|
||||||
|
packAttrs["anchor"] = W
|
||||||
self.widgets[key].pack(packAttrs)
|
self.widgets[key].pack(packAttrs)
|
||||||
|
|
||||||
return self, settings
|
return self, settings
|
||||||
|
|||||||
@@ -182,6 +182,7 @@ def roll_settings(weights):
|
|||||||
ret.enemy_damage = damage_choice
|
ret.enemy_damage = damage_choice
|
||||||
|
|
||||||
ret.enemy_health = get_choice('enemy_health')
|
ret.enemy_health = get_choice('enemy_health')
|
||||||
|
ret.any_enemy_logic = get_choice('any_enemy_logic')
|
||||||
|
|
||||||
ret.beemizer = get_choice('beemizer') if 'beemizer' in weights else '0'
|
ret.beemizer = get_choice('beemizer') if 'beemizer' in weights else '0'
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user