2025-03-07 19:43:02 -05:00
import logging
2024-01-12 14:32:15 -05:00
from dataclasses import dataclass
2025-03-07 19:43:02 -05:00
from typing import Dict , Any , TYPE_CHECKING
from decimal import Decimal , ROUND_HALF_UP
2024-06-01 06:34:41 -05:00
from Options import ( DefaultOnToggle , Toggle , StartInventoryPool , Choice , Range , TextChoice , PlandoConnections ,
2025-01-15 18:17:07 -05:00
PerGameCommonOptions , OptionGroup , Visibility , NamedRange )
2024-06-01 06:34:41 -05:00
from . er_data import portal_mapping
2025-03-07 19:43:02 -05:00
if TYPE_CHECKING :
from . import TunicWorld
2024-01-12 14:32:15 -05:00
class SwordProgression ( DefaultOnToggle ) :
2024-05-21 18:12:52 -04:00
"""
Adds four sword upgrades to the item pool that will progressively grant stronger melee weapons , including two new swords with increased range and attack power .
"""
2024-01-12 14:32:15 -05:00
internal_name = " sword_progression "
display_name = " Sword Progression "
class StartWithSword ( Toggle ) :
2024-05-21 18:12:52 -04:00
"""
Start with a sword in the player ' s inventory. Does not count towards Sword Progression.
"""
2024-01-12 14:32:15 -05:00
internal_name = " start_with_sword "
display_name = " Start With Sword "
class KeysBehindBosses ( Toggle ) :
2024-05-21 18:12:52 -04:00
"""
Places the three hexagon keys behind their respective boss fight in your world .
2025-03-07 19:43:02 -05:00
If playing Hexagon Quest , it will place three gold hexagons at the boss locations .
2024-05-21 18:12:52 -04:00
"""
2024-01-12 14:32:15 -05:00
internal_name = " keys_behind_bosses "
display_name = " Keys Behind Bosses "
2025-01-17 12:30:00 -05:00
class AbilityShuffling ( DefaultOnToggle ) :
2024-05-21 18:12:52 -04:00
"""
Locks the usage of Prayer , Holy Cross * , and the Icebolt combo until the relevant pages of the manual have been found .
2025-03-07 19:43:02 -05:00
If playing Hexagon Quest , abilities are instead randomly unlocked after obtaining 25 % , 50 % , and 75 % of the required
Hexagon goal amount , unless the option is set to have them unlock via pages instead .
2024-05-21 18:12:52 -04:00
* Certain Holy Cross usages are still allowed , such as the free bomb codes , the seeking spell , and other player - facing codes .
2024-01-12 14:32:15 -05:00
"""
internal_name = " ability_shuffling "
2024-03-15 08:53:41 -04:00
display_name = " Shuffle Abilities "
2024-01-12 14:32:15 -05:00
class Lanternless ( Toggle ) :
2024-05-21 18:12:52 -04:00
"""
Choose whether you require the Lantern for dark areas .
When enabled , the Lantern is marked as Useful instead of Progression .
"""
2024-01-12 14:32:15 -05:00
internal_name = " lanternless "
display_name = " Lanternless "
class Maskless ( Toggle ) :
2024-05-21 18:12:52 -04:00
"""
Choose whether you require the Scavenger ' s Mask for Lower Quarry.
When enabled , the Scavenger ' s Mask is marked as Useful instead of Progression.
"""
2024-01-12 14:32:15 -05:00
internal_name = " maskless "
display_name = " Maskless "
class FoolTraps ( Choice ) :
2024-05-21 18:12:52 -04:00
"""
Replaces low - to - medium value money rewards in the item pool with fool traps , which cause random negative effects to the player .
"""
2024-01-12 14:32:15 -05:00
internal_name = " fool_traps "
display_name = " Fool Traps "
option_off = 0
option_normal = 1
option_double = 2
option_onslaught = 3
default = 1
class HexagonQuest ( Toggle ) :
2024-05-21 18:12:52 -04:00
"""
An alternate goal that shuffles Gold " Questagon " items into the item pool and allows the game to be completed after collecting the required number of them .
"""
2024-01-12 14:32:15 -05:00
internal_name = " hexagon_quest "
display_name = " Hexagon Quest "
class HexagonGoal ( Range ) :
2024-05-21 18:12:52 -04:00
"""
How many Gold Questagons are required to complete the game on Hexagon Quest .
"""
2024-01-12 14:32:15 -05:00
internal_name = " hexagon_goal "
display_name = " Gold Hexagons Required "
2025-03-07 19:43:02 -05:00
range_start = 1
range_end = 100
2024-01-12 14:32:15 -05:00
default = 20
class ExtraHexagonPercentage ( Range ) :
2024-05-21 18:12:52 -04:00
"""
How many extra Gold Questagons are shuffled into the item pool , taken as a percentage of the goal amount .
2025-03-07 19:43:02 -05:00
The max number of Gold Questagons that can be in the item pool is 100 , so this option may be overridden and / or
reduced if the Hexagon Goal amount is greater than 50.
2024-05-21 18:12:52 -04:00
"""
2024-01-12 14:32:15 -05:00
internal_name = " extra_hexagon_percentage "
display_name = " Percentage of Extra Gold Hexagons "
range_start = 0
range_end = 100
default = 50
2025-03-07 19:43:02 -05:00
class HexagonQuestAbilityUnlockType ( Choice ) :
"""
Determines how abilities are unlocked when playing Hexagon Quest with Shuffled Abilities enabled .
Hexagons : A new ability is randomly unlocked after obtaining 25 % , 50 % , and 75 % of the required Hexagon goal amount . Requires at least 3 Gold Hexagons in the item pool , or 15 if Keys Behind Bosses is enabled .
Pages : Abilities are unlocked by finding specific pages in the manual .
This option does nothing if Shuffled Abilities is not enabled .
"""
internal_name = " hexagon_quest_ability_type "
display_name = " Hexagon Quest Ability Unlocks "
option_hexagons = 0
option_pages = 1
default = 0
2024-03-15 12:52:05 -04:00
class EntranceRando ( TextChoice ) :
2024-03-21 11:50:07 -04:00
"""
Randomize the connections between scenes .
A small , very lost fox on a big adventure .
2025-03-07 19:43:02 -05:00
2024-05-03 01:21:27 -04:00
If you set this option ' s value to a string, it will be used as a custom seed.
Every player who uses the same custom seed will have the same entrances , choosing the most restrictive settings among these players for the purpose of pairing entrances .
2024-03-21 11:50:07 -04:00
"""
2024-01-12 14:32:15 -05:00
internal_name = " entrance_rando "
display_name = " Entrance Rando "
2024-03-15 12:52:05 -04:00
alias_false = 0
2024-08-14 10:55:02 -04:00
alias_off = 0
2024-03-15 12:52:05 -04:00
option_no = 0
alias_true = 1
2024-08-14 10:55:02 -04:00
alias_on = 1
2024-03-15 12:52:05 -04:00
option_yes = 1
default = 0
2024-01-12 14:32:15 -05:00
class FixedShop ( Toggle ) :
2024-05-21 18:12:52 -04:00
"""
Forces the Windmill entrance to lead to a shop , and removes the remaining shops from the pool .
2024-05-19 19:01:24 -04:00
Adds another entrance in Rooted Ziggurat Lower to keep an even number of entrances .
2024-05-21 18:12:52 -04:00
Has no effect if Entrance Rando is not enabled .
"""
2024-01-12 14:32:15 -05:00
internal_name = " fixed_shop "
2024-03-15 08:53:41 -04:00
display_name = " Fewer Shops in Entrance Rando "
2024-01-12 14:32:15 -05:00
class LaurelsLocation ( Choice ) :
2024-05-21 18:12:52 -04:00
"""
Force the Hero ' s Laurels to be placed at a location in your world.
For if you want to avoid or specify early or late Laurels .
"""
2024-01-12 14:32:15 -05:00
internal_name = " laurels_location "
display_name = " Laurels Location "
option_anywhere = 0
option_6_coins = 1
option_10_coins = 2
option_10_fairies = 3
default = 0
2024-03-21 11:50:07 -04:00
class ShuffleLadders ( Toggle ) :
2024-05-21 18:12:52 -04:00
"""
Turns several ladders in the game into items that must be found before they can be climbed on .
2024-03-21 11:50:07 -04:00
Adds more layers of progression to the game by blocking access to many areas early on .
2024-05-21 18:12:52 -04:00
" Ladders were a mistake. "
— Andrew Shouldice
"""
2024-03-21 11:50:07 -04:00
internal_name = " shuffle_ladders "
display_name = " Shuffle Ladders "
2024-09-08 08:42:59 -04:00
2025-01-15 18:17:07 -05:00
class GrassRandomizer ( Toggle ) :
"""
Turns over 6 , 000 blades of grass and bushes in the game into checks .
"""
internal_name = " grass_randomizer "
display_name = " Grass Randomizer "
class LocalFill ( NamedRange ) :
"""
Choose the percentage of your filler / trap items that will be kept local or distributed to other TUNIC players with this option enabled .
If you have Grass Randomizer enabled , this option must be set to 95 % or higher to avoid flooding the item pool . The host can remove this restriction by turning off the limit_grass_rando setting in host . yaml .
This option defaults to 95 % if you have Grass Randomizer enabled , and to 0 % otherwise .
This option ignores items placed in your local_items or non_local_items .
This option does nothing in single player games .
"""
internal_name = " local_fill "
display_name = " Local Fill Percent "
range_start = 0
2025-01-21 12:39:08 -05:00
range_end = 98
2025-01-15 18:17:07 -05:00
special_range_names = {
" default " : - 1
}
default = - 1
visibility = Visibility . template | Visibility . complex_ui | Visibility . spoiler
2024-06-03 04:44:37 -04:00
class TunicPlandoConnections ( PlandoConnections ) :
2024-06-15 23:02:48 -04:00
"""
Generic connection plando . Format is :
- entrance : " Entrance Name "
exit : " Exit Name "
percentage : 100
Percentage is an integer from 0 to 100 which determines whether that connection will be made . Defaults to 100 if omitted .
"""
2024-06-01 06:34:41 -05:00
entrances = { * ( portal . name for portal in portal_mapping ) , " Shop " , " Shop Portal " }
exits = { * ( portal . name for portal in portal_mapping ) , " Shop " , " Shop Portal " }
duplicate_exits = True
2024-03-21 11:50:07 -04:00
2024-12-15 16:40:36 -05:00
class CombatLogic ( Choice ) :
"""
If enabled , the player will logically require a combination of stat upgrade items and equipment to get some checks or navigate to some areas , with a goal of matching the vanilla combat difficulty .
The player may still be expected to run past enemies , reset aggro ( by using a checkpoint or doing a scene transition ) , or find sneaky paths to checks .
This option marks many more items as progression and may force weapons much earlier than normal .
Bosses Only makes it so that additional combat logic is only added to the boss fights and the Gauntlet .
If disabled , the standard , looser logic is used . The standard logic does not include stat upgrades , just minimal weapon requirements , such as requiring a Sword or Magic Wand for Quarry , or not requiring a weapon for Swamp .
"""
internal_name = " combat_logic "
display_name = " More Combat Logic "
option_off = 0
option_bosses_only = 1
option_on = 2
default = 0
2024-09-08 08:42:59 -04:00
class LaurelsZips ( Toggle ) :
"""
Choose whether to include using the Hero ' s Laurels to zip through gates, doors, and tricky spots.
Notable inclusions are the Monastery gate , Ruined Passage door , Old House gate , Forest Grave Path gate , and getting from the Back of Swamp to the Middle of Swamp .
"""
internal_name = " laurels_zips "
display_name = " Laurels Zips Logic "
class IceGrappling ( Choice ) :
"""
Choose whether grappling frozen enemies is in logic .
Easy includes ice grappling enemies that are in range without luring them . May include clips through terrain .
Medium includes using ice grapples to push enemies through doors or off ledges without luring them . Also includes bringing an enemy over to the Temple Door to grapple through it .
Hard includes luring or grappling enemies to get to where you want to go .
2024-10-28 21:43:03 -04:00
Enabling any of these difficulty options will give the player the Torch to return to the Overworld checkpoint to avoid softlocks . Using the Torch is considered in logic .
2024-09-08 08:42:59 -04:00
Note : You will still be expected to ice grapple to the slime in East Forest from below with this option off .
"""
internal_name = " ice_grappling "
display_name = " Ice Grapple Logic "
option_off = 0
option_easy = 1
option_medium = 2
option_hard = 3
default = 0
class LadderStorage ( Choice ) :
"""
Choose whether Ladder Storage is in logic .
Easy includes uses of Ladder Storage to get to open doors over a long distance without too much difficulty . May include convenient elevation changes ( going up Mountain stairs , stairs in front of Special Shop , etc . ) .
Medium includes the above as well as changing your elevation using the environment and getting knocked down by melee enemies mid - LS .
Hard includes the above as well as going behind the map to enter closed doors from behind , shooting a fuse with the magic wand to knock yourself down at close range , and getting into the Cathedral Secret Legend room mid - LS .
2024-10-28 21:43:03 -04:00
Enabling any of these difficulty options will give the player the Torch to return to the Overworld checkpoint to avoid softlocks . Using the Torch is considered in logic .
2024-09-08 08:42:59 -04:00
Opening individual chests while doing ladder storage is excluded due to tedium .
Knocking yourself out of LS with a bomb is excluded due to the problematic nature of consumables in logic .
"""
internal_name = " ladder_storage "
display_name = " Ladder Storage Logic "
option_off = 0
option_easy = 1
option_medium = 2
option_hard = 3
default = 0
class LadderStorageWithoutItems ( Toggle ) :
"""
2024-12-08 19:58:49 -05:00
If disabled , you logically require Stick , Sword , Magic Orb , or Shield to perform Ladder Storage .
2024-09-08 08:42:59 -04:00
If enabled , you will be expected to perform Ladder Storage without progression items .
This can be done with the plushie code , a Golden Coin , Prayer , and many other options .
This option has no effect if you do not have Ladder Storage Logic enabled .
"""
internal_name = " ladder_storage_without_items "
display_name = " Ladder Storage without Items "
class LogicRules ( Choice ) :
"""
This option has been superseded by the individual trick options .
If set to nmg , it will set Ice Grappling to medium and Laurels Zips on .
If set to ur , it will do nmg as well as set Ladder Storage to medium .
It is here to avoid breaking old yamls , and will be removed at a later date .
"""
visibility = Visibility . none
internal_name = " logic_rules "
display_name = " Logic Rules "
option_restricted = 0
option_no_major_glitches = 1
alias_nmg = 1
option_unrestricted = 2
alias_ur = 2
default = 0
2025-03-08 11:25:47 -05:00
class BreakableShuffle ( Toggle ) :
"""
Turns approximately 250 breakable objects in the game into checks .
"""
internal_name = " breakable_shuffle "
display_name = " Breakable Shuffle "
2024-01-12 14:32:15 -05:00
@dataclass
class TunicOptions ( PerGameCommonOptions ) :
2024-05-19 19:01:24 -04:00
start_inventory_from_pool : StartInventoryPool
2025-01-17 12:30:00 -05:00
2024-01-12 14:32:15 -05:00
sword_progression : SwordProgression
start_with_sword : StartWithSword
keys_behind_bosses : KeysBehindBosses
ability_shuffling : AbilityShuffling
fool_traps : FoolTraps
2025-01-17 12:30:00 -05:00
laurels_location : LaurelsLocation
2024-01-12 14:32:15 -05:00
hexagon_quest : HexagonQuest
hexagon_goal : HexagonGoal
extra_hexagon_percentage : ExtraHexagonPercentage
2025-03-07 19:43:02 -05:00
hexagon_quest_ability_type : HexagonQuestAbilityUnlockType
2025-01-17 12:30:00 -05:00
shuffle_ladders : ShuffleLadders
grass_randomizer : GrassRandomizer
2025-03-08 11:25:47 -05:00
breakable_shuffle : BreakableShuffle
2025-01-17 12:30:00 -05:00
local_fill : LocalFill
entrance_rando : EntranceRando
fixed_shop : FixedShop
2024-12-15 16:40:36 -05:00
combat_logic : CombatLogic
2024-01-12 14:32:15 -05:00
lanternless : Lanternless
maskless : Maskless
2024-09-08 08:42:59 -04:00
laurels_zips : LaurelsZips
ice_grappling : IceGrappling
ladder_storage : LadderStorage
ladder_storage_without_items : LadderStorageWithoutItems
2025-01-17 12:30:00 -05:00
2024-06-03 04:44:37 -04:00
plando_connections : TunicPlandoConnections
2025-01-17 12:30:00 -05:00
2024-09-08 08:42:59 -04:00
logic_rules : LogicRules
2025-01-17 12:30:00 -05:00
2024-05-21 18:12:52 -04:00
tunic_option_groups = [
2025-03-07 19:43:02 -05:00
OptionGroup ( " Hexagon Quest Options " , [
HexagonQuest ,
HexagonGoal ,
ExtraHexagonPercentage ,
HexagonQuestAbilityUnlockType
] ) ,
2024-05-21 18:12:52 -04:00
OptionGroup ( " Logic Options " , [
2024-12-15 16:40:36 -05:00
CombatLogic ,
2024-05-21 18:12:52 -04:00
Lanternless ,
Maskless ,
2024-09-08 08:42:59 -04:00
LaurelsZips ,
IceGrappling ,
LadderStorage ,
LadderStorageWithoutItems
2024-05-21 18:12:52 -04:00
] )
]
2024-05-22 20:12:59 -04:00
tunic_option_presets : Dict [ str , Dict [ str , Any ] ] = {
" Sync " : {
" ability_shuffling " : True ,
} ,
" Async " : {
" progression_balancing " : 0 ,
" ability_shuffling " : True ,
" shuffle_ladders " : True ,
" laurels_location " : " 10_fairies " ,
} ,
" Glace Mode " : {
" accessibility " : " minimal " ,
" ability_shuffling " : True ,
2024-09-08 08:42:59 -04:00
" entrance_rando " : True ,
2024-05-22 20:12:59 -04:00
" fool_traps " : " onslaught " ,
2024-09-08 08:42:59 -04:00
" laurels_zips " : True ,
" ice_grappling " : " hard " ,
" ladder_storage " : " hard " ,
" ladder_storage_without_items " : True ,
2024-05-22 20:12:59 -04:00
" maskless " : True ,
" lanternless " : True ,
} ,
}
2025-03-07 19:43:02 -05:00
def check_options ( world : " TunicWorld " ) :
options = world . options
if options . hexagon_quest and options . ability_shuffling and options . hexagon_quest_ability_type == HexagonQuestAbilityUnlockType . option_hexagons :
total_hexes = get_hexagons_in_pool ( world )
min_hexes = 3
if options . keys_behind_bosses :
min_hexes = 15
if total_hexes < min_hexes :
logging . warning ( f " TUNIC: Not enough Gold Hexagons in { world . player_name } ' s item pool for Hexagon Ability Shuffle with the selected options. Ability Shuffle mode will be switched to Pages. " )
options . hexagon_quest_ability_type . value = HexagonQuestAbilityUnlockType . option_pages
def get_hexagons_in_pool ( world : " TunicWorld " ) :
# Calculate number of hexagons in item pool
options = world . options
return min ( int ( ( Decimal ( 100 + options . extra_hexagon_percentage ) / 100 * options . hexagon_goal )
. to_integral_value ( rounding = ROUND_HALF_UP ) ) , 100 )