309 lines
14 KiB
Python
309 lines
14 KiB
Python
# OpenMFOR
|
|
# credits manually reinstated due to the comments being lost from the object code decompilation
|
|
|
|
# Original release is Copyright (C) 2022 Kazuto88
|
|
# This program is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
|
|
# Source Generated with Decompyle++
|
|
# File: GUI.pyc (Python 3.8)
|
|
|
|
import PySimpleGUI as ui
|
|
import zlib
|
|
import time
|
|
import threading
|
|
import os
|
|
|
|
ui.theme('SystemDefault')
|
|
tt_race = ' Generating a race seed will not generate a spoiler log. '
|
|
tt_createpatch = ' Automatically creates a .bps patch after generating a randomized game, so you can easily share with others. '
|
|
tt_difficulty = ' Higher difficulty means harder tricks. Difficulty 0 means no tricks are expected outside of vanilla strategies. '
|
|
tt_itempool = ' Limit where important items can be placed. If checked, major upgrades will only be in Data rooms, \n at bosses, or at vanilla E-tank locations. If unchecked, any item can be anywhere within logic. '
|
|
tt_missileswithupgrades = ' If checked, collecting any major Missile item will enable Missiles. Logic will account for this setting. '
|
|
tt_pbswithoutbombs = ' If checked, Power Bombs can be used without having regular Bombs. Logic will account for this setting. '
|
|
tt_damageruns = ' Allow logic to require Samus to run through damaging environments to acquire important items. \n Examples include heated rooms, electrified water, and lava. '
|
|
tt_splitsecurity = ' If checked, each door color must be unlocked independently. \n If unchecked, unlocking higher security levels will unlock all security levels below it. \n E.G.: unlocking Level 2 (Green) doors will also unlock Level 1 (Blue) doors. '
|
|
tt_SectorShuffle = ' Randomly shuffle the arrangement of the Sectors. '
|
|
tt_HideItems = ' Make all items appear as ? tanks. '
|
|
generating = False
|
|
|
|
def fileHash(fileName):
|
|
with open(fileName, 'rb') as fh:
|
|
hash = 0
|
|
while True:
|
|
s = fh.read(65536)
|
|
if not s:
|
|
break
|
|
hash = zlib.crc32(s, hash)
|
|
return hash & 0xFFFFFFFF
|
|
|
|
def rando_thread(window = None, values = None):
|
|
global failedgen, randoerror, generating, fileName, basegame, failedgen, randoerror, generating, finishGenerating
|
|
values.update({
|
|
'Difficulty': int(values.get('Difficulty')) })
|
|
if values['Num']:
|
|
values.update({
|
|
'Num': int(values.get('Num')) })
|
|
else:
|
|
values.update({
|
|
'Num': 1 })
|
|
if values['Num'] < 1:
|
|
values.update({
|
|
'Num': 1 })
|
|
values.pop(0)
|
|
failedgen = False
|
|
randoerror = False
|
|
generating = True
|
|
|
|
from Randomizer import start_randomizer
|
|
if values['Debug']:
|
|
fileName = start_randomizer(basegame, values)
|
|
else:
|
|
# FIXME: let it explode
|
|
#try:
|
|
fileName = start_randomizer(basegame, values)
|
|
# finally:
|
|
# pass
|
|
#except SystemExit:
|
|
# failedgen = True
|
|
#except:
|
|
# print('An error occurred, no game was generated.')
|
|
# randoerror = True
|
|
|
|
|
|
generating = False
|
|
finishGenerating = True
|
|
|
|
|
|
def main_window(debug):
|
|
global fileName, finishGenerating, basegame, finishGenerating
|
|
itemPool = [
|
|
[
|
|
ui.Text('Charge Beam'),
|
|
ui.Radio('Shuffled', 'radioChargeBeam', default=True),
|
|
ui.Radio('Starting', 'radioChargeBeam'),
|
|
ui.Radio('Disabled', 'radioChargeBeam')],
|
|
[
|
|
ui.Text('Wide Beam'),
|
|
ui.Radio('Shuffled', 'radioWideBeam', default=True),
|
|
ui.Radio('Starting', 'radioWideBeam'),
|
|
ui.Radio('Disabled', 'radioWideBeam')],
|
|
[
|
|
ui.Text('Plasma Beam'),
|
|
ui.Radio('Shuffled', 'radioPlasmaBeam', default=True),
|
|
ui.Radio('Starting', 'radioPlasmaBeam'),
|
|
ui.Radio('Disabled', 'radioPlasmaBeam')],
|
|
[
|
|
ui.Text('Wave Beam'),
|
|
ui.Radio('Shuffled', 'radioWaveBeam', default=True),
|
|
ui.Radio('Starting', 'radioWaveBeam'),
|
|
ui.Radio('Disabled', 'radioWaveBeam')],
|
|
[
|
|
ui.Text('Ice Beam'),
|
|
ui.Radio('Shuffled', 'radioIceBeam', default=True),
|
|
ui.Radio('Starting', 'radioIceBeam'),
|
|
ui.Radio('Disabled', 'radioIceBeam')],
|
|
[
|
|
ui.Text('Missile Data'),
|
|
ui.Radio('Shuffled', 'radioMissileData', default=True),
|
|
ui.Radio('Starting', 'radioMissileData'),
|
|
ui.Radio('Disabled', 'radioMissileData')],
|
|
[
|
|
ui.Text('Super Missile Data'),
|
|
ui.Radio('Shuffled', 'radioSuperMissile', default=True),
|
|
ui.Radio('Starting', 'radioSuperMissile'),
|
|
ui.Radio('Disabled', 'radioSuperMissile')],
|
|
[
|
|
ui.Text('Ice Missile Data'),
|
|
ui.Radio('Shuffled', 'radioIceMissile', default=True),
|
|
ui.Radio('Starting', 'radioIceMissile'),
|
|
ui.Radio('Disabled', 'radioIceMissile')],
|
|
[
|
|
ui.Text('Diffusion Data'),
|
|
ui.Radio('Shuffled', 'radioDiffusion', default=True),
|
|
ui.Radio('Starting', 'radioDiffusion'),
|
|
ui.Radio('Disabled', 'radioDiffusion')],
|
|
[
|
|
ui.Text('Bombs'),
|
|
ui.Radio('Shuffled', 'radioBombs', default=True),
|
|
ui.Radio('Starting', 'radioBombs'),
|
|
ui.Radio('Disabled', 'radioBombs')],
|
|
[
|
|
ui.Text('Power Bomb Data'),
|
|
ui.Radio('Shuffled', 'radioPowerBombData', default=True),
|
|
ui.Radio('Starting', 'radioPowerBombData'),
|
|
ui.Radio('Disabled', 'radioPowerBombData')],
|
|
[
|
|
ui.Text('Hi-Jump Boots'),
|
|
ui.Radio('Shuffled', 'radioHiJump', default=True),
|
|
ui.Radio('Starting', 'radioHiJump'),
|
|
ui.Radio('Disabled', 'radioHiJump')],
|
|
[
|
|
ui.Text('Speed Booster'),
|
|
ui.Radio('Shuffled', 'radioSpeedBooster', default=True),
|
|
ui.Radio('Starting', 'radioSpeedBooster'),
|
|
ui.Radio('Disabled', 'radioSpeedBooster')],
|
|
[
|
|
ui.Text('Space Jump'),
|
|
ui.Radio('Shuffled', 'radioSpaceJump', default=True),
|
|
ui.Radio('Starting', 'radioSpaceJump'),
|
|
ui.Radio('Disabled', 'radioSpaceJump')],
|
|
[
|
|
ui.Text('Screw Attack'),
|
|
ui.Radio('Shuffled', 'radioScrewAttack', default=True),
|
|
ui.Radio('Starting', 'radioScrewAttack'),
|
|
ui.Radio('Disabled', 'radioScrewAttack')],
|
|
[
|
|
ui.Text('Varia Suit'),
|
|
ui.Radio('Shuffled', 'radioVariaSuit', default=True),
|
|
ui.Radio('Starting', 'radioVariaSuit'),
|
|
ui.Radio('Disabled', 'radioVariaSuit')],
|
|
[
|
|
ui.Text('Gravity Suit'),
|
|
ui.Radio('Shuffled', 'radioGravitySuit', default=True),
|
|
ui.Radio('Starting', 'radioGravitySuit'),
|
|
ui.Radio('Disabled', 'radioGravitySuit')],
|
|
[
|
|
ui.Text('Morph Ball'),
|
|
ui.Radio('Shuffled', 'radioMorphBall', default=True),
|
|
ui.Radio('Starting', 'radioMorphBall'),
|
|
ui.Radio('Disabled', 'radioMorphBall')],
|
|
[
|
|
ui.Text('Blue Doors (Level 1)'),
|
|
ui.Radio('Standard', 'radioBlueDoors', default=True),
|
|
ui.Radio('Starting', 'radioBlueDoors'),
|
|
ui.Radio('Shuffled', 'radioBlueDoors')],
|
|
[
|
|
ui.Text('Green Doors (Level 2)'),
|
|
ui.Radio('Standard', 'radioGreenDoors', default=True),
|
|
ui.Radio('Starting', 'radioGreenDoors'),
|
|
ui.Radio('Shuffled', 'radioGreenDoors')],
|
|
[
|
|
ui.Text('Yellow Doors (Level 3)'),
|
|
ui.Radio('Standard', 'radioYellowDoors', default=True),
|
|
ui.Radio('Starting', 'radioYellowDoors'),
|
|
ui.Radio('Shuffled', 'radioYellowDoors')],
|
|
[
|
|
ui.Text('Red Doors (Level 4)'),
|
|
ui.Radio('Standard', 'radioRedDoors', default=True),
|
|
ui.Radio('Starting', 'radioRedDoors'),
|
|
ui.Radio('Shuffled', 'radioRedDoors')]]
|
|
settings = [
|
|
[
|
|
ui.Text('Difficulty'),
|
|
ui.Slider(range=(0, 5), orientation='h', tooltip=tt_difficulty, key='Difficulty')],
|
|
[
|
|
ui.Checkbox(text='Major/minor item split', key='MajorMinor', tooltip=tt_itempool)],
|
|
[
|
|
ui.Checkbox(text='Missile upgrades enable Missiles', key='MissilesWithoutMainData', tooltip=tt_missileswithupgrades)],
|
|
[
|
|
ui.Checkbox(text='Enable Power Bombs without Bombs', key='PowerBombsWithoutBombs', tooltip=tt_pbswithoutbombs)],
|
|
[
|
|
ui.Checkbox(text='Damage runs', key='DamageRuns', tooltip=tt_damageruns)],
|
|
[
|
|
ui.Checkbox(text='Split security levels', key='SplitSecurity', tooltip=tt_splitsecurity)],
|
|
[
|
|
ui.Checkbox(text='Sector Shuffle', key='SectorShuffle', tooltip=tt_SectorShuffle)],
|
|
[
|
|
ui.Checkbox(text='Hide item graphics', key='HideItems', tooltip=tt_HideItems)],
|
|
[
|
|
ui.Checkbox(text='Race seed', key='RaceSeed', tooltip=tt_race)]]
|
|
tabLayout = [
|
|
[
|
|
ui.TabGroup([
|
|
[
|
|
ui.Tab('Logic', settings)]], tab_location='topleft')]]
|
|
layout = [
|
|
[
|
|
ui.Text('Seed value'),
|
|
ui.Input(key='Seed')],
|
|
[
|
|
tabLayout],
|
|
[
|
|
ui.Text('Number of Seeds to generate:'),
|
|
ui.Input(key='Num', size=5, justification='center', enable_events=True),
|
|
ui.Push(),
|
|
ui.Checkbox(text='Create patch', key='Patch', tooltip=tt_createpatch),
|
|
ui.Button(button_text='Generate', bind_return_key=True)]]
|
|
window = ui.Window('Open Metroid Fusion Open Randomizer', layout)
|
|
fileName = str()
|
|
finishGenerating = False
|
|
oldNum = str()
|
|
while True:
|
|
(event, values) = window.read()
|
|
if event == ui.WINDOW_CLOSED:
|
|
break
|
|
|
|
elif event == 'Num':
|
|
if len(values['Num']) > 3:
|
|
window['Num'].update(oldNum)
|
|
for x in range(4):
|
|
if x < len(values['Num']):
|
|
if values['Num'][x] not in '0123456789':
|
|
window['Num'].update(oldNum)
|
|
break
|
|
else:
|
|
oldNum = values['Num']
|
|
|
|
elif event == 'Generate':
|
|
basegame = ui.popup_get_file('Choose base game', no_titlebar=True, file_types=[('GBA File', '*.gba')], history=True, history_setting_filename=os.path.join('.', 'settings.json'))
|
|
if basegame == '':
|
|
ui.popup('Please select a Metroid Fusion (U) rom.', title='No source rom selected')
|
|
if basegame != None and basegame != '':
|
|
checksum = fileHash(basegame)
|
|
if checksum != 1819625372:
|
|
ui.popup('Only Metroid Fusion (U) is supported.\nCheck the CRC32 value: it should be 6C75479C', title='Bad Checksum')
|
|
else:
|
|
values.update({
|
|
'Debug': debug })
|
|
threading.Thread(target=rando_thread, args=[window, values], daemon=True).start()
|
|
window['Difficulty'].update(disabled=True)
|
|
window['MajorMinor'].update(disabled=True)
|
|
window['MissilesWithoutMainData'].update(disabled=True)
|
|
window['PowerBombsWithoutBombs'].update(disabled=True)
|
|
window['DamageRuns'].update(disabled=True)
|
|
window['SplitSecurity'].update(disabled=True)
|
|
window['SectorShuffle'].update(disabled=True)
|
|
window['HideItems'].update(disabled=True)
|
|
window['Seed'].update(disabled=True)
|
|
window['RaceSeed'].update(disabled=True)
|
|
window['Num'].update(disabled=True)
|
|
window['Patch'].update(disabled=True)
|
|
window['Generate'].update(disabled=True)
|
|
|
|
if generating:
|
|
ui.popup_animated(ui.DEFAULT_BASE64_LOADING_GIF, 'Generating game, please wait...', time_between_frames=20)
|
|
window.Refresh()
|
|
|
|
elif finishGenerating:
|
|
ui.popup_animated(None)
|
|
if failedgen:
|
|
ui.popup('Could not generate a game with the current settings. Try changing your settings.', title='Metroid Fusion Open Randomizer')
|
|
elif randoerror:
|
|
ui.popup('An error occurred, no game was generated.', title='Error')
|
|
elif ui.Input.get(window['Num']) != '':
|
|
if int(ui.Input.get(window['Num'])) > 1:
|
|
ui.popup('Multiple games have been added to the seeds folder.', title='Success!')
|
|
else:
|
|
ui.popup('{}\nhas been added to the seeds folder.'.format(fileName), title='Success!')
|
|
else:
|
|
ui.popup('{}\nhas been added to the seeds folder.'.format(fileName), title='Success!')
|
|
window['Difficulty'].update(disabled=False)
|
|
window['MajorMinor'].update(disabled=False)
|
|
window['MissilesWithoutMainData'].update(disabled=False)
|
|
window['PowerBombsWithoutBombs'].update(disabled=False)
|
|
window['DamageRuns'].update(disabled=False)
|
|
window['SplitSecurity'].update(disabled=False)
|
|
window['SectorShuffle'].update(disabled=False)
|
|
window['HideItems'].update(disabled=False)
|
|
window['Seed'].update(disabled=False)
|
|
window['RaceSeed'].update(disabled=False)
|
|
window['Num'].update(disabled=False)
|
|
window['Patch'].update(disabled=False)
|
|
window['Generate'].update(disabled=False)
|
|
finishGenerating = False
|
|
window.close()
|
|
|