Merge branch 'DoorDevUnstable' into DoorDevVolatile
# Conflicts: # Main.py # Rom.py # data/base2current.bps
This commit is contained in:
@@ -97,6 +97,7 @@ class World(object):
|
||||
set_player_attr('player_names', [])
|
||||
set_player_attr('remote_items', False)
|
||||
set_player_attr('required_medallions', ['Ether', 'Quake'])
|
||||
set_player_attr('bottle_refills', ['Bottle (Green Potion)', 'Bottle (Green Potion)'])
|
||||
set_player_attr('swamp_patch_required', False)
|
||||
set_player_attr('powder_patch_required', False)
|
||||
set_player_attr('ganon_at_pyramid', True)
|
||||
@@ -2287,6 +2288,7 @@ class Spoiler(object):
|
||||
self.doorTypes = {}
|
||||
self.lobbies = {}
|
||||
self.medallions = {}
|
||||
self.bottles = {}
|
||||
self.playthrough = {}
|
||||
self.unreachables = []
|
||||
self.startinventory = []
|
||||
@@ -2330,6 +2332,15 @@ class Spoiler(object):
|
||||
self.medallions[f'Misery Mire ({self.world.get_player_names(player)})'] = self.world.required_medallions[player][0]
|
||||
self.medallions[f'Turtle Rock ({self.world.get_player_names(player)})'] = self.world.required_medallions[player][1]
|
||||
|
||||
self.bottles = OrderedDict()
|
||||
if self.world.players == 1:
|
||||
self.bottles['Waterfall Bottle'] = self.world.bottle_refills[1][0]
|
||||
self.bottles['Pyramid Bottle'] = self.world.bottle_refills[1][1]
|
||||
else:
|
||||
for player in range(1, self.world.players + 1):
|
||||
self.bottles[f'Waterfall Bottle ({self.world.get_player_names(player)})'] = self.world.bottle_refills[player][0]
|
||||
self.bottles[f'Pyramid Bottle ({self.world.get_player_names(player)})'] = self.world.bottle_refills[player][1]
|
||||
|
||||
self.startinventory = list(map(str, self.world.precollected_items))
|
||||
|
||||
self.locations = OrderedDict()
|
||||
@@ -2452,6 +2463,7 @@ class Spoiler(object):
|
||||
out.update(self.locations)
|
||||
out['Starting Inventory'] = self.startinventory
|
||||
out['Special'] = self.medallions
|
||||
out['Bottles'] = self.bottles
|
||||
if self.hashes:
|
||||
out['Hashes'] = {f"{self.world.player_names[player][team]} (Team {team+1})": hash for (player, team), hash in self.hashes.items()}
|
||||
if self.shops:
|
||||
@@ -2543,6 +2555,9 @@ class Spoiler(object):
|
||||
outfile.write('\n\nMedallions:\n')
|
||||
for dungeon, medallion in self.medallions.items():
|
||||
outfile.write(f'\n{dungeon}: {medallion} Medallion')
|
||||
outfile.write('\n\nBottle Refills:\n')
|
||||
for fairy, bottle in self.bottles.items():
|
||||
outfile.write(f'\n{fairy}: {bottle}')
|
||||
if self.startinventory:
|
||||
outfile.write('\n\nStarting Inventory:\n\n')
|
||||
outfile.write('\n'.join(self.startinventory))
|
||||
|
||||
@@ -370,6 +370,15 @@ def generate_itempool(world, player):
|
||||
tr_medallion = ['Ether', 'Quake', 'Bombos'][random.randint(0, 2)]
|
||||
world.required_medallions[player] = (mm_medallion, tr_medallion)
|
||||
|
||||
# shuffle bottle refills
|
||||
if world.difficulty[player] in ['hard', 'expert']:
|
||||
waterfall_bottle = hardbottles[random.randint(0, 5)]
|
||||
pyramid_bottle = hardbottles[random.randint(0, 5)]
|
||||
else:
|
||||
waterfall_bottle = normalbottles[random.randint(0, 6)]
|
||||
pyramid_bottle = normalbottles[random.randint(0, 6)]
|
||||
world.bottle_refills[player] = (waterfall_bottle, pyramid_bottle)
|
||||
|
||||
set_up_shops(world, player)
|
||||
|
||||
if world.retro[player]:
|
||||
|
||||
1
Main.py
1
Main.py
@@ -369,6 +369,7 @@ def copy_world(world):
|
||||
ret.player_names = copy.deepcopy(world.player_names)
|
||||
ret.remote_items = world.remote_items.copy()
|
||||
ret.required_medallions = world.required_medallions.copy()
|
||||
ret.bottle_refills = world.bottle_refills.copy()
|
||||
ret.swamp_patch_required = world.swamp_patch_required.copy()
|
||||
ret.ganon_at_pyramid = world.ganon_at_pyramid.copy()
|
||||
ret.powder_patch_required = world.powder_patch_required.copy()
|
||||
|
||||
65
Mystery.py
65
Mystery.py
@@ -101,7 +101,8 @@ def get_weights(path):
|
||||
raise Exception(f'Failed to read weights file: {e}')
|
||||
|
||||
def roll_settings(weights):
|
||||
def get_choice(option, root=weights):
|
||||
def get_choice(option, root=None):
|
||||
root = weights if root is None else root
|
||||
if option not in root:
|
||||
return None
|
||||
if type(root[option]) is not dict:
|
||||
@@ -116,13 +117,24 @@ def roll_settings(weights):
|
||||
return default
|
||||
return choice
|
||||
|
||||
while True:
|
||||
subweights = weights.get('subweights', {})
|
||||
if len(subweights) == 0:
|
||||
break
|
||||
chances = ({k: int(v['chance']) for (k, v) in subweights.items()})
|
||||
subweight_name = random.choices(list(chances.keys()), weights=list(chances.values()))[0]
|
||||
subweights = weights.get('subweights', {}).get(subweight_name, {}).get('weights', {})
|
||||
subweights['subweights'] = subweights.get('subweights', {})
|
||||
weights = {**weights, **subweights}
|
||||
|
||||
ret = argparse.Namespace()
|
||||
|
||||
glitches_required = get_choice('glitches_required')
|
||||
if glitches_required not in ['none', 'no_logic']:
|
||||
print("Only NMG and No Logic supported")
|
||||
glitches_required = 'none'
|
||||
ret.logic = {'none': 'noglitches', 'no_logic': 'nologic'}[glitches_required]
|
||||
if glitches_required is not None:
|
||||
if glitches_required not in ['none', 'no_logic']:
|
||||
print("Only NMG and No Logic supported")
|
||||
glitches_required = 'none'
|
||||
ret.logic = {'none': 'noglitches', 'no_logic': 'nologic'}[glitches_required]
|
||||
|
||||
item_placement = get_choice('item_placement')
|
||||
# not supported in ER
|
||||
@@ -157,26 +169,27 @@ def roll_settings(weights):
|
||||
ret.standardize_palettes = get_choice('standardize_palettes') if 'standardize_palettes' in weights else 'standardize'
|
||||
|
||||
goal = get_choice('goals')
|
||||
ret.goal = {'ganon': 'ganon',
|
||||
'fast_ganon': 'crystals',
|
||||
'dungeons': 'dungeons',
|
||||
'pedestal': 'pedestal',
|
||||
'triforce-hunt': 'triforcehunt'
|
||||
}[goal]
|
||||
if goal is not None:
|
||||
ret.goal = {'ganon': 'ganon',
|
||||
'fast_ganon': 'crystals',
|
||||
'dungeons': 'dungeons',
|
||||
'pedestal': 'pedestal',
|
||||
'triforce-hunt': 'triforcehunt'
|
||||
}[goal]
|
||||
ret.openpyramid = goal == 'fast_ganon' if ret.shuffle in ['vanilla', 'dungeonsfull', 'dungeonssimple'] else False
|
||||
|
||||
ret.crystals_gt = get_choice('tower_open')
|
||||
|
||||
ret.crystals_ganon = get_choice('ganon_open')
|
||||
|
||||
if ret.goal == 'triforcehunt':
|
||||
goal_min = get_choice_default('triforce_goal_min', default=20)
|
||||
goal_max = get_choice_default('triforce_goal_max', default=20)
|
||||
pool_min = get_choice_default('triforce_pool_min', default=30)
|
||||
pool_max = get_choice_default('triforce_pool_max', default=30)
|
||||
ret.triforce_goal = random.randint(int(goal_min), int(goal_max))
|
||||
min_diff = get_choice_default('triforce_min_difference', default=10)
|
||||
ret.triforce_pool = random.randint(max(int(pool_min), ret.triforce_goal + int(min_diff)), int(pool_max))
|
||||
|
||||
goal_min = get_choice_default('triforce_goal_min', default=20)
|
||||
goal_max = get_choice_default('triforce_goal_max', default=20)
|
||||
pool_min = get_choice_default('triforce_pool_min', default=30)
|
||||
pool_max = get_choice_default('triforce_pool_max', default=30)
|
||||
ret.triforce_goal = random.randint(int(goal_min), int(goal_max))
|
||||
min_diff = get_choice_default('triforce_min_difference', default=10)
|
||||
ret.triforce_pool = random.randint(max(int(pool_min), ret.triforce_goal + int(min_diff)), int(pool_max))
|
||||
|
||||
ret.mode = get_choice('world_state')
|
||||
if ret.mode == 'retro':
|
||||
ret.mode = 'open'
|
||||
@@ -187,11 +200,13 @@ def roll_settings(weights):
|
||||
|
||||
ret.hints = get_choice('hints') == 'on'
|
||||
|
||||
ret.swords = {'randomized': 'random',
|
||||
'assured': 'assured',
|
||||
'vanilla': 'vanilla',
|
||||
'swordless': 'swordless'
|
||||
}[get_choice('weapons')]
|
||||
swords = get_choice('weapons')
|
||||
if swords is not None:
|
||||
ret.swords = {'randomized': 'random',
|
||||
'assured': 'assured',
|
||||
'vanilla': 'vanilla',
|
||||
'swordless': 'swordless'
|
||||
}[swords]
|
||||
|
||||
ret.difficulty = get_choice('item_pool')
|
||||
|
||||
|
||||
@@ -47,7 +47,13 @@ CLI: ```--bombbag```
|
||||
* Fix for hard pool capacity upgrades missing
|
||||
* Bonk Fairy (Light) is no longer in logic for ER Standard and is forbidden to be a connector, so rain state isn't exitable
|
||||
* Bug fix for retro + enemizer and arrows appearing under pots
|
||||
* Add bombbag and shufflelinks to settings code
|
||||
* Added bombbag and shufflelinks to settings code
|
||||
* Catobat fixes:
|
||||
* Fairy refills in spoiler
|
||||
* Subweights support in mystery
|
||||
* More defaults for mystery weights
|
||||
* Less camera jank for straight stair transitions
|
||||
* Bug with Straight stairs with vanilla doors where Link's walking animation stopped early is fixed
|
||||
* 0.5.1.4
|
||||
* Revert quadrant glitch fix for baserom
|
||||
* Fix for inverted
|
||||
|
||||
10
Rom.py
10
Rom.py
@@ -32,7 +32,7 @@ from source.classes.SFX import randomize_sfx
|
||||
|
||||
|
||||
JAP10HASH = '03a63945398191337e896e5771f77173'
|
||||
RANDOMIZERBASEHASH = '513b5a20d5e42ece59d5794aa242d46a'
|
||||
RANDOMIZERBASEHASH = '2cfa164d4b66a15406f53ca4750ef59a'
|
||||
|
||||
|
||||
class JsonRom(object):
|
||||
@@ -1066,12 +1066,8 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
|
||||
])
|
||||
|
||||
# set Fountain bottle exchange items
|
||||
if world.difficulty[player] in ['hard', 'expert']:
|
||||
rom.write_byte(0x348FF, [0x16, 0x2B, 0x2C, 0x2D, 0x3C, 0x48][random.randint(0, 5)])
|
||||
rom.write_byte(0x3493B, [0x16, 0x2B, 0x2C, 0x2D, 0x3C, 0x48][random.randint(0, 5)])
|
||||
else:
|
||||
rom.write_byte(0x348FF, [0x16, 0x2B, 0x2C, 0x2D, 0x3C, 0x3D, 0x48][random.randint(0, 6)])
|
||||
rom.write_byte(0x3493B, [0x16, 0x2B, 0x2C, 0x2D, 0x3C, 0x3D, 0x48][random.randint(0, 6)])
|
||||
rom.write_byte(0x348FF, ItemFactory(world.bottle_refills[player][0], player).code)
|
||||
rom.write_byte(0x3493B, ItemFactory(world.bottle_refills[player][1], player).code)
|
||||
|
||||
#enable Fat Fairy Chests
|
||||
rom.write_bytes(0x1FC16, [0xB1, 0xC6, 0xF9, 0xC9, 0xC6, 0xF9])
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
; Normal doors use $FE to store the trap door indicator
|
||||
; Normal doors use $045e to store Y coordinate when transitioning to in-room stairs
|
||||
; Normal doors use $045f to determine the order in which supertile quadrants are drawn
|
||||
; Straight stairs use $046d to store X coordinate on animation start
|
||||
; Spiral doors use $045e to store stair type
|
||||
; Gfx uses $b1 to for sub-sub-sub-module thing
|
||||
|
||||
|
||||
@@ -681,6 +681,8 @@ db $00,$07,$20,$20,$07,$07,$07,$07,$07,$20,$20,$07,$20,$20,$20,$20
|
||||
db $07,$07,$02,$02,$02,$02,$07,$07,$07,$20,$20,$07,$20,$20,$20,$07
|
||||
|
||||
;27f300
|
||||
DungeonTilesets:
|
||||
db $04,$04,$05,$12,$04,$08,$07,$0C,$09,$0B,$05,$0A,$0D,$0E,$06,$06
|
||||
|
||||
;
|
||||
;org $27ff00
|
||||
|
||||
@@ -150,15 +150,14 @@ LoadRoomVert:
|
||||
.notEdge
|
||||
lda $01 : and #$03 : cmp #$03 : bne .normal
|
||||
jsr ScrollToInroomStairs
|
||||
stz $046d
|
||||
bra .end
|
||||
.normal
|
||||
ldy #$01 : jsr ShiftVariablesMainDir
|
||||
jsr PrepScrollToNormal
|
||||
.scroll
|
||||
lda $01 : and #$40 : pha
|
||||
lda $01 : and #$40 : sta $046d
|
||||
jsr ScrollX
|
||||
pla : beq .end
|
||||
ldy #$00 : jsr ApplyScroll
|
||||
.end
|
||||
plb ; restore db register
|
||||
rts
|
||||
@@ -291,6 +290,11 @@ StraightStairsAdj:
|
||||
stx $0464 : sty $012e ; what we wrote over
|
||||
lda.l DRMode : beq +
|
||||
lda $045e : bne .toInroom
|
||||
lda $046d : beq .noScroll
|
||||
sta $22
|
||||
ldy #$00 : jsr ApplyScroll
|
||||
stz $046d
|
||||
.noScroll
|
||||
jsr GetTileAttribute : tax
|
||||
lda $11 : cmp #$12 : beq .goingNorth
|
||||
lda $a2 : cmp #$51 : bne ++
|
||||
@@ -338,9 +342,10 @@ db $d0, $f6, $10, $1a, $f0, $00
|
||||
|
||||
StraightStairsFix:
|
||||
{
|
||||
pha
|
||||
lda.l DRMode : bne +
|
||||
!add $20 : sta $20 ;what we wrote over
|
||||
+ rtl
|
||||
pla : !add $20 : sta $20 : rtl ;what we wrote over
|
||||
+ pla : rtl
|
||||
}
|
||||
|
||||
StraightStairLayerFix:
|
||||
|
||||
@@ -168,7 +168,11 @@ ScrollX: ;change the X offset variables
|
||||
|
||||
pla : sta $00
|
||||
sep #$30
|
||||
lda $04 : sta $22
|
||||
lda $04 : ldx $046d : bne .straight
|
||||
sta $22 : bra +
|
||||
.straight
|
||||
sta $046d ; set X position later
|
||||
+
|
||||
lda $00 : sta $23 : sta $0609 : sta $060d
|
||||
lda $01 : sta $a9
|
||||
lda $0e : asl : ora $ac : sta $ac
|
||||
|
||||
Binary file not shown.
57
mystery_example_subweights.yml
Normal file
57
mystery_example_subweights.yml
Normal file
@@ -0,0 +1,57 @@
|
||||
description: Example for subweights
|
||||
glitches_required: none
|
||||
world_state: open
|
||||
goals: ganon
|
||||
weapons: randomized
|
||||
entrance_shuffle: none
|
||||
intensity: 3
|
||||
subweights:
|
||||
vanilla:
|
||||
chance: 25
|
||||
weights:
|
||||
door_shuffle: vanilla
|
||||
keydropshuffle:
|
||||
on: 40
|
||||
off: 60
|
||||
basic:
|
||||
chance: 25
|
||||
weights:
|
||||
door_shuffle: basic
|
||||
keydropshuffle:
|
||||
on: 70
|
||||
off: 30
|
||||
crossed:
|
||||
chance: 25
|
||||
weights:
|
||||
door_shuffle: crossed
|
||||
keydropshuffle:
|
||||
on: 90
|
||||
off: 10
|
||||
chaos:
|
||||
chance: 25
|
||||
weights:
|
||||
door_shuffle: crossed
|
||||
entrance_shuffle:
|
||||
none: 30
|
||||
crossed: 70
|
||||
keydropshuffle:
|
||||
on: 90
|
||||
off: 10
|
||||
shopsanity:
|
||||
on: 50
|
||||
off: 50
|
||||
bombbag:
|
||||
on: 25
|
||||
off: 75
|
||||
subweights:
|
||||
normal:
|
||||
chance: 40
|
||||
weights: {}
|
||||
swordless:
|
||||
chance: 20
|
||||
weights:
|
||||
weapons: swordless
|
||||
keysanity:
|
||||
chance: 40
|
||||
weights:
|
||||
dungeon_items: full
|
||||
Reference in New Issue
Block a user