diff --git a/AdjusterMain.py b/AdjusterMain.py index c6f19680..72b0a1c5 100644 --- a/AdjusterMain.py +++ b/AdjusterMain.py @@ -35,7 +35,7 @@ def adjust(args): args.sprite, args.ow_palettes, args.uw_palettes, args.reduce_flashing, args.shuffle_sfx, args.msu_resume) - output_path.cached_path = args.outputpath + # output_path.cached_path = args.outputpath rom.write_to_file(output_path('%s.sfc' % outfilebase)) logger.info('Done. Enjoy.') diff --git a/BaseClasses.py b/BaseClasses.py index 6d60a94c..498f3e3c 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -105,6 +105,7 @@ class World(object): self.sanc_portal = {} self.fish = BabelFish() self.pot_contents = {} + self.trolls = {} for player in range(1, players + 1): def set_player_attr(attr, val): @@ -177,6 +178,7 @@ class World(object): set_player_attr('exp_cache', defaultdict(dict)) set_player_attr('enabled_entrances', {}) + set_player_attr('trolls', False) def finish_init(self): for player in range(1, self.players + 1): @@ -184,6 +186,8 @@ class World(object): self.mode[player] = 'open' if self.goal[player] == 'completionist': self.accessibility[player] = 'locations' + if self.trolls[player]: + self.can_take_damage[player] = False def get_name_string_for_object(self, obj): return obj.name if self.players == 1 else f'{obj.name} ({self.get_player_names(obj.player)})' @@ -352,7 +356,7 @@ class World(object): return True else: return False - + def check_for_door(self, doorname, player): if isinstance(doorname, Door): return doorname diff --git a/Bosses.py b/Bosses.py index f003565d..f12e92a8 100644 --- a/Bosses.py +++ b/Bosses.py @@ -38,6 +38,8 @@ def LanmolasDefeatRule(state, player): state.has_special_weapon_level(player, 1))) def MoldormDefeatRule(state, player): + if state.world.trolls[player] and not (state.has('Blue Boomerang', player) or state.has('Red Boomerang', player) or state.has('Hookshot', player)): + return False return (state.special_weapon_check(player, 1) and (state.has_blunt_weapon(player) or state.has_special_weapon_level(player, 1))) @@ -74,6 +76,8 @@ def MothulaDefeatRule(state, player): state.has_special_weapon_level(player, 1))) def BlindDefeatRule(state, player): + if state.world.trolls[player]: + return state.has('Shovel', player) return (state.special_weapon_check(player, 1) and (state.has_blunt_weapon(player) or state.has('Cane of Somaria', player) or state.has('Cane of Byrna', player) or state.has_special_weapon_level(player, 1))) @@ -107,6 +111,14 @@ def VitreousDefeatRule(state, player): state.has_special_weapon_level(player, 2))) def TrinexxDefeatRule(state, player): + if state.world.trolls[player]: + if not (state.has('Bombos', player) and state.has('Ether', player)): + return False + return (state.has('Hammer', player) or + state.has_real_sword(player, 3) or + state.has_special_weapon_level(player, 4) or + ((state.has_real_sword(player, 2) or state.has_special_weapon_level(player, 3)) + and state.can_extend_magic(player, 24))) if not (state.has('Fire Rod', player) and state.has('Ice Rod', player)): return False if not state.special_weapon_check(player, 2): diff --git a/CLI.py b/CLI.py index ccb4ecf5..4dcdd571 100644 --- a/CLI.py +++ b/CLI.py @@ -147,7 +147,7 @@ def parse_cli(argv, no_defaults=False): 'heartbeep', 'remote_items', 'shopsanity', 'dropshuffle', 'pottery', 'keydropshuffle', 'mixed_travel', 'standardize_palettes', 'code', 'reduce_flashing', 'shuffle_sfx', 'msu_resume', 'collection_rate', 'colorizepots', 'decoupledoors', 'door_type_mode', - 'bonk_drops', 'trap_door_mode', 'key_logic_algorithm']: + 'bonk_drops', 'trap_door_mode', 'key_logic_algorithm', 'trolls']: value = getattr(defaults, name) if getattr(playerargs, name) is None else getattr(playerargs, name) if player == 1: setattr(ret, name, {1: value}) @@ -360,7 +360,8 @@ def parse_settings(): "outputpath": os.path.join("."), "saveonexit": "ask", "outputname": "", - "startinventoryarray": {} + "startinventoryarray": {}, + "trolls": False, } if sys.platform.lower().find("windows"): diff --git a/ItemList.py b/ItemList.py index 85db3aea..d893c0d9 100644 --- a/ItemList.py +++ b/ItemList.py @@ -1126,6 +1126,10 @@ def get_pool_core(world, player, progressive, shuffle, difficulty, treasure_hunt pool.remove('Piece of Heart') pool.extend(['Rupees (20)'] * 12) + if world.trolls[player]: + pool.remove('Rupees (20)') + pool.extend(['Magic Upgrade (1/2)']) + if bombbag: pool = [item.replace('Bomb Upgrade (+5)','Rupees (5)') for item in pool] pool = [item.replace('Bomb Upgrade (+10)','Rupees (5)') for item in pool] diff --git a/Rom.py b/Rom.py index 67ef2bff..6a25893e 100644 --- a/Rom.py +++ b/Rom.py @@ -2715,6 +2715,46 @@ def write_strings(rom, world, player, team): rom.write_byte(0x04a529, 0x19) # x tile shifted right a few tiles rom.write_byte(0x04a52e, 0x06) # follower set to blind maiden + if world.trolls[player]: + tt['telepathic_tile_eastern_palace'] = "Health will not help you against the Armos Knights." + tt['telepathic_tile_desert_bonk_torch_room'] = "Who ever saw lanmolas fly so fast?" + tt['telepathic_tile_tower_of_hera_entrance'] = "Please stop tickling my tail. It makes me freeze up and lower my defenses.\n\n - Moldorm" + tt['telepathic_tile_tower_of_hera_floor_4'] = "Hookshots and boomerangs are forbidden in my tower.\n\n - Moldorm" + tt['telepathic_tile_spectacle_rock'] = "With greater health and armor come shorter periods of refuge from damage." + tt['telepathic_tile_castle_tower'] = "Agahnim does not care for your foolish mortal patterns." + tt['telepathic_tile_palace_of_darkness'] = "Who weakened my mask?\n\n - Helmasaur King" + tt['telepathic_tile_swamp_entrance'] = "Please take your boots off before entering my room.\n\n - Arrghus" + tt['telepathic_tile_thieves_town_upstairs'] = "Secret power is said to be in the shovel." + tt['blind_by_the_light'] = "I'd really dig it if you didn't bring a shovel." + tt['telepathic_tile_ice_entrace'] = "Kholdstare's hypnotic eyes can be confusing..." + tt['telepathic_tile_ice_stalfos_knights_room'] = "Bomb fuses may be shorter or longer than they appear." + tt['telepathic_tile_ice_large_room'] = "Does your sword feel heavier than usual?" + tt['telepathic_tile_misery_mire'] = "Vitreous' small eyes require explosives to defeat." + tt['telepathic_tile_turtle_rock'] = "Trinexx has upped her defenses, requiring more powerful spells to defeat her. Consider whether you have enough magic reserves." + + ganon_mock = 'Can you keep up with my changing weaknesses?'; + tt['ganon_phase_3_no_bow'] = ganon_mock + tt['ganon_phase_3_no_silvers'] = ganon_mock + tt['ganon_phase_3_no_silvers_alt'] = ganon_mock + tt['ganon_phase_3_silvers'] = ganon_mock + + tt.insertText('ganon_fall_in_mushroom', "I smell a mushroom!") + tt.insertText('ganon_phase_3_mushroom', "Your mushroom grants me invulnerability!") + tt.insertText('ganon_phase_4_silvers', "\n SILVER ARROWS ") + tt.insertText('ganon_phase_4_boomerang', "\n BOOMERANG ") + tt.insertText('ganon_phase_4_hookshot', "\n HOOKSHOT ") + tt.insertText('ganon_phase_4_bomb', "\n BOMB ") + tt.insertText('ganon_phase_4_powder', "\n POWDER ") + tt.insertText('ganon_phase_4_fire_rod', "\n FIRE ROD ") + tt.insertText('ganon_phase_4_ice_rod', "\n ICE ROD ") + tt.insertText('ganon_phase_4_bombos', "\n BOMBOS ") + tt.insertText('ganon_phase_4_ether', "\n ETHER ") + tt.insertText('ganon_phase_4_quake', "\n QUAKE ") + tt.insertText('ganon_phase_4_hammer', "\n HAMMER ") + tt.insertText('ganon_phase_4_bee', "\n BEE ") + tt.insertText('ganon_phase_4_somaria', "\n SOMARIA ") + tt.insertText('ganon_phase_4_byrna', "\n BYRNA ") + # inverted spawn menu changes if world.mode[player] == 'inverted': tt['menu_start_2'] = "{MENU}\n{SPEED0}\n≥@'s House\n Dark Chapel\n{CHOICE3}" diff --git a/Text.py b/Text.py index 5d023818..9fc7d86b 100644 --- a/Text.py +++ b/Text.py @@ -1390,6 +1390,16 @@ class TextTable(object): return data.ljust(self.SIZE, b'\xff') return data + def insertText(self, key, value): + if key in self._text: + raise KeyError(key) + if isinstance(value, str): + self._text[key] = CompressedTextMapper.convert(value) + else: + self._text[key] = value + self._text.move_to_end('end_pad_data') + self._text.move_to_end('terminator') + def removeUnwantedText(self): nomessage = bytes(CompressedTextMapper.convert("{NOTEXT}", False)) messages_to_zero = [ diff --git a/resources/app/cli/args.json b/resources/app/cli/args.json index 1e200ee0..177615e3 100644 --- a/resources/app/cli/args.json +++ b/resources/app/cli/args.json @@ -603,5 +603,9 @@ ] }, "outputname": {}, - "code": {} + "code": {}, + "trolls": { + "action": "store_true", + "type": "bool" + } }