Added Super Metroid support (#46)

Varia Randomizer based implementation
LttPClient -> SNIClient
This commit is contained in:
lordlou
2021-11-12 08:00:11 -05:00
committed by GitHub
parent 61ae51b30c
commit 77ec8d4141
141 changed files with 43859 additions and 106 deletions

View File

@@ -0,0 +1,397 @@
import random
import copy
from logic.smbool import SMBool
from rom.rom_patches import RomPatches
import utils.log, logging
LOG = utils.log.get('DoorsManager')
colorsList = ['red', 'green', 'yellow', 'wave', 'spazer', 'plasma', 'ice']
# 1/15 chance to have the door set to grey
colorsListGrey = colorsList * 2 + ['grey']
class Facing:
Left = 0
Right = 1
Top = 2
Bottom = 3
# door facing left - right - top - bottom
plmRed = [0xc88a, 0xc890, 0xc896, 0xc89c]
plmGreen = [0xc872, 0xc878, 0xc87e, 0xc884]
plmYellow = [0xc85a, 0xc860, 0xc866, 0xc86c]
plmGrey = [0xc842, 0xc848, 0xc84e, 0xc854]
plmWave = [0xf763, 0xf769, 0xf70f, 0xf715]
plmSpazer = [0xf733, 0xf739, 0xf73f, 0xf745]
plmPlasma = [0xf74b, 0xf751, 0xf757, 0xf75d]
plmIce = [0xf71b, 0xf721, 0xf727, 0xf72d]
colors2plm = {
'red': plmRed,
'green': plmGreen,
'yellow': plmYellow,
'grey': plmGrey,
'wave': plmWave,
'spazer': plmSpazer,
'plasma': plmPlasma,
'ice': plmIce
}
class Door(object):
__slots__ = ('name', 'address', 'vanillaColor', 'color', 'forced', 'facing', 'hidden', 'id', 'canGrey', 'forbiddenColors')
def __init__(self, name, address, vanillaColor, facing, id=None, canGrey=False, forbiddenColors=None):
self.name = name
self.address = address
self.vanillaColor = vanillaColor
self.setColor(vanillaColor)
self.forced = False
self.facing = facing
self.hidden = False
self.canGrey = canGrey
self.id = id
# list of forbidden colors
self.forbiddenColors = forbiddenColors
def forceBlue(self):
# custom start location, area, patches can force doors to blue
self.setColor('blue')
self.forced = True
def setColor(self, color):
self.color = color
def getColor(self):
if self.hidden:
return 'grey'
else:
return self.color
def isRandom(self):
return self.color != self.vanillaColor and not self.isBlue()
def isBlue(self):
return self.color == 'blue'
def canRandomize(self):
return not self.forced and self.id is None
def filterColorList(self, colorsList):
if self.forbiddenColors is None:
return colorsList
else:
return [color for color in colorsList if color not in self.forbiddenColors]
def randomize(self, allowGreyDoors):
if self.canRandomize():
if self.canGrey and allowGreyDoors:
self.setColor(random.choice(self.filterColorList(colorsListGrey)))
else:
self.setColor(random.choice(self.filterColorList(colorsList)))
def traverse(self, smbm):
if self.hidden or self.color == 'grey':
return SMBool(False)
elif self.color == 'red':
return smbm.canOpenRedDoors()
elif self.color == 'green':
return smbm.canOpenGreenDoors()
elif self.color == 'yellow':
return smbm.canOpenYellowDoors()
elif self.color == 'wave':
return smbm.haveItem('Wave')
elif self.color == 'spazer':
return smbm.haveItem('Spazer')
elif self.color == 'plasma':
return smbm.haveItem('Plasma')
elif self.color == 'ice':
return smbm.haveItem('Ice')
else:
return SMBool(True)
def __repr__(self):
return "Door({}, {})".format(self.name, self.color)
def isRefillSave(self):
return self.address is None
def writeColor(self, rom):
if self.isBlue() or self.isRefillSave():
return
rom.writeWord(colors2plm[self.color][self.facing], self.address)
# also set plm args high byte to never opened, even during escape
if self.color == 'grey':
rom.writeByte(0x90, self.address+5)
def readColor(self, rom):
if self.forced or self.isRefillSave():
return
plm = rom.readWord(self.address)
if plm in plmRed:
self.setColor('red')
elif plm in plmGreen:
self.setColor('green')
elif plm in plmYellow:
self.setColor('yellow')
elif plm in plmGrey:
self.setColor('grey')
elif plm in plmWave:
self.setColor('wave')
elif plm in plmSpazer:
self.setColor('spazer')
elif plm in plmPlasma:
self.setColor('plasma')
elif plm in plmIce:
self.setColor('ice')
else:
raise Exception("Unknown color {} for {}".format(hex(plm), self.name))
# for tracker
def canHide(self):
return self.color != 'blue'
def hide(self):
if self.canHide():
self.hidden = True
def reveal(self):
self.hidden = False
def switch(self):
if self.hidden:
self.reveal()
else:
self.hide()
# to send/receive state to tracker/plando
def serialize(self):
return (self.color, self.facing, self.hidden)
def unserialize(self, data):
self.setColor(data[0])
self.facing = data[1]
self.hidden = data[2]
class DoorsManager():
doorsDict = {}
doors = {
# crateria
'LandingSiteRight': Door('LandingSiteRight', 0x78018, 'green', Facing.Left, canGrey=True),
'LandingSiteTopRight': Door('LandingSiteTopRight', 0x07801e, 'yellow', Facing.Left),
'KihunterBottom': Door('KihunterBottom', 0x78228, 'yellow', Facing.Top, canGrey=True),
'KihunterRight': Door('KihunterRight', 0x78222, 'yellow', Facing.Left, canGrey=True),
'FlywayRight': Door('FlywayRight', 0x78420, 'red', Facing.Left),
'GreenPiratesShaftBottomRight': Door('GreenPiratesShaftBottomRight', 0x78470, 'red', Facing.Left, canGrey=True),
'RedBrinstarElevatorTop': Door('RedBrinstarElevatorTop', 0x78256, 'yellow', Facing.Bottom),
'ClimbRight': Door('ClimbRight', 0x78304, 'yellow', Facing.Left),
# blue brinstar
'ConstructionZoneRight': Door('ConstructionZoneRight', 0x78784, 'red', Facing.Left),
# green brinstar
'GreenHillZoneTopRight': Door('GreenHillZoneTopRight', 0x78670, 'yellow', Facing.Left, canGrey=True),
'NoobBridgeRight': Door('NoobBridgeRight', 0x787a6, 'green', Facing.Left, canGrey=True),
'MainShaftRight': Door('MainShaftRight', 0x784be, 'red', Facing.Left),
'MainShaftBottomRight': Door('MainShaftBottomRight', 0x784c4, 'red', Facing.Left, canGrey=True),
'EarlySupersRight': Door('EarlySupersRight', 0x78512, 'red', Facing.Left),
'EtecoonEnergyTankLeft': Door('EtecoonEnergyTankLeft', 0x787c8, 'green', Facing.Right),
# pink brinstar
'BigPinkTopRight': Door('BigPinkTopRight', 0x78626, 'red', Facing.Left),
'BigPinkRight': Door('BigPinkRight', 0x7861a, 'yellow', Facing.Left),
'BigPinkBottomRight': Door('BigPinkBottomRight', 0x78620, 'green', Facing.Left, canGrey=True),
'BigPinkBottomLeft': Door('BigPinkBottomLeft', 0x7862c, 'red', Facing.Right),
# red brinstar
'RedTowerLeft': Door('RedTowerLeft', 0x78866, 'yellow', Facing.Right),
'RedBrinstarFirefleaLeft': Door('RedBrinstarFirefleaLeft', 0x7886e, 'red', Facing.Right),
'RedTowerElevatorTopLeft': Door('RedTowerElevatorTopLeft', 0x788aa, 'green', Facing.Right),
'RedTowerElevatorLeft': Door('RedTowerElevatorLeft', 0x788b0, 'yellow', Facing.Right),
'RedTowerElevatorBottomLeft': Door('RedTowerElevatorBottomLeft', 0x788b6, 'green', Facing.Right),
'BelowSpazerTopRight': Door('BelowSpazerTopRight', 0x78966, 'green', Facing.Left),
# Wrecked ship
'WestOceanRight': Door('WestOceanRight', 0x781e2, 'green', Facing.Left, canGrey=True),
'LeCoudeBottom': Door('LeCoudeBottom', 0x7823e, 'yellow', Facing.Top, canGrey=True),
'WreckedShipMainShaftBottom': Door('WreckedShipMainShaftBottom', 0x7c277, 'green', Facing.Top),
'ElectricDeathRoomTopLeft': Door('ElectricDeathRoomTopLeft', 0x7c32f, 'red', Facing.Right),
# Upper Norfair
'BusinessCenterTopLeft': Door('BusinessCenterTopLeft', 0x78b00, 'green', Facing.Right),
'BusinessCenterBottomLeft': Door('BusinessCenterBottomLeft', 0x78b0c, 'red', Facing.Right),
'CathedralEntranceRight': Door('CathedralEntranceRight', 0x78af2, 'red', Facing.Left, canGrey=True),
'CathedralRight': Door('CathedralRight', 0x78aea, 'green', Facing.Left),
'BubbleMountainTopRight': Door('BubbleMountainTopRight', 0x78c60, 'green', Facing.Left),
'BubbleMountainTopLeft': Door('BubbleMountainTopLeft', 0x78c5a, 'green', Facing.Right),
'SpeedBoosterHallRight': Door('SpeedBoosterHallRight', 0x78c7a, 'red', Facing.Left),
'SingleChamberRight': Door('SingleChamberRight', 0x78ca8, 'red', Facing.Left),
'DoubleChamberRight': Door('DoubleChamberRight', 0x78cc2, 'red', Facing.Left),
'KronicBoostBottomLeft': Door('KronicBoostBottomLeft', 0x78d4e, 'yellow', Facing.Right, canGrey=True),
'CrocomireSpeedwayBottom': Door('CrocomireSpeedwayBottom', 0x78b96, 'green', Facing.Top, canGrey=True),
# Crocomire
'PostCrocomireUpperLeft': Door('PostCrocomireUpperLeft', 0x78bf4, 'red', Facing.Right),
'PostCrocomireShaftRight': Door('PostCrocomireShaftRight', 0x78c0c, 'red', Facing.Left),
# Lower Norfair
'RedKihunterShaftBottom': Door('RedKihunterShaftBottom', 0x7902e, 'yellow', Facing.Top),
'WastelandLeft': Door('WastelandLeft', 0x790ba, 'green', Facing.Right, forbiddenColors=['yellow']),
# Maridia
'MainStreetBottomRight': Door('MainStreetBottomRight', 0x7c431, 'red', Facing.Left),
'FishTankRight': Door('FishTankRight', 0x7c475, 'red', Facing.Left),
'CrabShaftRight': Door('CrabShaftRight', 0x7c4fb, 'green', Facing.Left),
'ColosseumBottomRight': Door('ColosseumBottomRight', 0x7c6fb, 'green', Facing.Left),
'PlasmaSparkBottom': Door('PlasmaSparkBottom', 0x7c577, 'green', Facing.Top),
'OasisTop': Door('OasisTop', 0x7c5d3, 'green', Facing.Bottom),
# refill/save
'GreenBrinstarSaveStation': Door('GreenBrinstarSaveStation', None, 'red', Facing.Right, id=0x1f),
'MaridiaBottomSaveStation': Door('MaridiaBottomSaveStation', None, 'red', Facing.Left, id=0x8c),
'MaridiaAqueductSaveStation': Door('MaridiaAqueductSaveStation', None, 'red', Facing.Right, id=0x96),
'ForgottenHighwaySaveStation': Door('ForgottenHighwaySaveStation', None, 'red', Facing.Left, id=0x92),
'DraygonSaveRefillStation': Door('DraygonSaveRefillStation', None, 'red', Facing.Left, id=0x98),
'KraidRefillStation': Door('KraidRefillStation', None, 'green', Facing.Left, id=0x44),
'RedBrinstarEnergyRefill': Door('RedBrinstarEnergyRefill', None, 'green', Facing.Right, id=0x38),
'GreenBrinstarMissileRefill': Door('GreenBrinstarMissileRefill', None, 'red', Facing.Right, id=0x23)
}
# call from logic
def traverse(self, smbm, doorName):
return DoorsManager.doorsDict[smbm.player][doorName].traverse(smbm)
@staticmethod
def setDoorsColor(player=0):
if player not in DoorsManager.doorsDict.keys():
DoorsManager.doorsDict[player] = copy.deepcopy(DoorsManager.doors)
currentDoors = DoorsManager.doorsDict[player]
# depending on loaded patches, force some doors to blue, excluding them from randomization
if RomPatches.has(player, RomPatches.BlueBrinstarBlueDoor):
currentDoors['ConstructionZoneRight'].forceBlue()
if RomPatches.has(player, RomPatches.BrinReserveBlueDoors):
currentDoors['MainShaftRight'].forceBlue()
currentDoors['EarlySupersRight'].forceBlue()
if RomPatches.has(player, RomPatches.EtecoonSupersBlueDoor):
currentDoors['EtecoonEnergyTankLeft'].forceBlue()
#if RomPatches.has(player, RomPatches.SpongeBathBlueDoor):
# currentDoors[''].forceBlue()
if RomPatches.has(player, RomPatches.HiJumpAreaBlueDoor):
currentDoors['BusinessCenterBottomLeft'].forceBlue()
if RomPatches.has(player, RomPatches.SpeedAreaBlueDoors):
currentDoors['BubbleMountainTopRight'].forceBlue()
currentDoors['SpeedBoosterHallRight'].forceBlue()
if RomPatches.has(player, RomPatches.MamaTurtleBlueDoor):
currentDoors['FishTankRight'].forceBlue()
if RomPatches.has(player, RomPatches.HellwayBlueDoor):
currentDoors['RedTowerElevatorLeft'].forceBlue()
if RomPatches.has(player, RomPatches.RedTowerBlueDoors):
currentDoors['RedBrinstarElevatorTop'].forceBlue()
if RomPatches.has(player, RomPatches.AreaRandoBlueDoors):
currentDoors['GreenHillZoneTopRight'].forceBlue()
currentDoors['NoobBridgeRight'].forceBlue()
currentDoors['LeCoudeBottom'].forceBlue()
currentDoors['KronicBoostBottomLeft'].forceBlue()
else:
# no area rando, prevent some doors to be in the grey doors pool
currentDoors['GreenPiratesShaftBottomRight'].canGrey = False
currentDoors['CrocomireSpeedwayBottom'].canGrey = False
currentDoors['KronicBoostBottomLeft'].canGrey = False
if RomPatches.has(player, RomPatches.AreaRandoMoreBlueDoors):
currentDoors['KihunterBottom'].forceBlue()
currentDoors['GreenPiratesShaftBottomRight'].forceBlue()
if RomPatches.has(player, RomPatches.CrocBlueDoors):
currentDoors['CrocomireSpeedwayBottom'].forceBlue()
if RomPatches.has(player, RomPatches.CrabShaftBlueDoor):
currentDoors['CrabShaftRight'].forceBlue()
@staticmethod
def randomize(allowGreyDoors, player):
for door in DoorsManager.doorsDict[player].values():
door.randomize(allowGreyDoors)
# set both ends of toilet to the same color to avoid soft locking in area rando
toiletTop = DoorsManager.doorsDict[player]['PlasmaSparkBottom']
toiletBottom = DoorsManager.doorsDict[player]['OasisTop']
if toiletTop.color != toiletBottom.color:
toiletBottom.setColor(toiletTop.color)
DoorsManager.debugDoorsColor()
# call from rom loader
@staticmethod
def loadDoorsColor(rom):
# force to blue some doors depending on patches
DoorsManager.setDoorsColor()
# for each door store it's color
for door in DoorsManager.doors.values():
door.readColor(rom)
DoorsManager.debugDoorsColor()
# tell that we have randomized doors
isRandom = DoorsManager.isRandom()
if isRandom:
DoorsManager.setRefillSaveToBlue()
return isRandom
@staticmethod
def isRandom(player):
return any(door.isRandom() for door in DoorsManager.doorsDict[player].values())
@staticmethod
def setRefillSaveToBlue(player):
for door in DoorsManager.doorsDict[player].values():
if door.id is not None:
door.forceBlue()
@staticmethod
def debugDoorsColor():
if LOG.getEffectiveLevel() == logging.DEBUG:
for door in DoorsManager.doors.values():
LOG.debug("{:>32}: {:>6}".format(door.name, door.color))
# call from rom patcher
@staticmethod
def writeDoorsColor(rom, doors, player):
for door in DoorsManager.doorsDict[player].values():
door.writeColor(rom)
# also set save/refill doors to blue
if door.id is not None:
doors.append(door.id)
# call from web
@staticmethod
def getAddressesToRead():
return [door.address for door in DoorsManager.doors.values() if door.address is not None] + [door.address+1 for door in DoorsManager.doors.values() if door.address is not None]
# for isolver state
@staticmethod
def serialize():
return {door.name: door.serialize() for door in DoorsManager.doors.values()}
@staticmethod
def unserialize(state):
for name, data in state.items():
DoorsManager.doors[name].unserialize(data)
@staticmethod
def allDoorsRevealed():
for door in DoorsManager.doors.values():
if door.hidden:
return False
return True
# when using the tracker, first set all colored doors to grey until the user clicks on it
@staticmethod
def initTracker():
for door in DoorsManager.doors.values():
door.hide()
# when the user clicks on a door in the tracker
@staticmethod
def switchVisibility(name):
DoorsManager.doors[name].switch()
# when the user clicks on a door in the race tracker or the plando
@staticmethod
def setColor(name, color):
# in race mode the doors are hidden
DoorsManager.doors[name].reveal()
DoorsManager.doors[name].setColor(color)
# in autotracker we need the current doors state
@staticmethod
def getDoorsState():
hiddenDoors = set([door.name for door in DoorsManager.doors.values() if door.hidden])
revealedDoor = set([door.name for door in DoorsManager.doors.values() if (not door.hidden) and door.canHide()])
return (hiddenDoors, revealedDoor)

View File

@@ -0,0 +1,16 @@
import logging, sys
# store the debug flag at module level
debug = False
def init(pdebug):
global debug
debug = pdebug
if debug == True:
logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)
else:
logging.basicConfig(level=logging.INFO)
def get(name):
return logging.getLogger(name)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,450 @@
import os, json, sys, re, random
from utils.parameters import Knows, Settings, Controller, isKnows, isSettings, isButton
from utils.parameters import easy, medium, hard, harder, hardcore, mania, text2diff
from logic.smbool import SMBool
from Utils import is_frozen
def isStdPreset(preset):
return preset in ['newbie', 'casual', 'regular', 'veteran', 'expert', 'master', 'samus', 'solution', 'Season_Races', 'SMRAT2021']
def getPresetDir(preset):
if isStdPreset(preset):
return 'lib/worlds/sm/variaRandomizer/standard_presets' if is_frozen() else 'worlds/sm/variaRandomizer/standard_presets'
else:
return 'lib/worlds/sm/variaRandomizer/community_presets' if is_frozen() else 'worlds/sm/variaRandomizer/community_presets'
def removeChars(string, toRemove):
return re.sub('[{}]+'.format(toRemove), '', string)
def range_union(ranges):
ret = []
for rg in sorted([[r.start, r.stop] for r in ranges]):
begin, end = rg[0], rg[-1]
if ret and ret[-1][1] > begin:
ret[-1][1] = max(ret[-1][1], end)
else:
ret.append([begin, end])
return [range(r[0], r[1]) for r in ret]
# https://github.com/robotools/fontParts/commit/7cb561033929cfb4a723d274672e7257f5e68237
def normalizeRounding(n):
# Normalizes rounding as Python 2 and Python 3 handing the rounding of halves (0.5, 1.5, etc) differently.
# This normalizes rounding to be the same in both environments.
if round(0.5) != 1 and n % 1 == .5 and not int(n) % 2:
return int((round(n) + (abs(n) / n) * 1))
else:
return int(round(n))
# gauss random in [0, r] range
# the higher the slope, the less probable extreme values are.
def randGaussBounds(r, slope=5):
r = float(r)
n = normalizeRounding(random.gauss(r/2, r/slope))
if n < 0:
n = 0
if n > r:
n = int(r)
return n
# from a relative weight dictionary, gives a normalized range dictionary
# example :
# { 'a' : 10, 'b' : 17, 'c' : 3 } => {'c': 0.1, 'a':0.4333333, 'b':1 }
def getRangeDict(weightDict):
total = float(sum(weightDict.values()))
rangeDict = {}
current = 0.0
for k in sorted(weightDict, key=weightDict.get):
w = float(weightDict[k]) / total
current += w
rangeDict[k] = current
return rangeDict
def chooseFromRange(rangeDict):
r = random.random()
val = None
for v in sorted(rangeDict, key=rangeDict.get):
val = v
if r < rangeDict[v]:
return v
return val
class PresetLoader(object):
@staticmethod
def factory(params):
# can be a json, a python file or a dict with the parameters
if type(params) == str:
ext = os.path.splitext(params)
if ext[1].lower() == '.json':
return PresetLoaderJson(params)
else:
raise Exception("PresetLoader: wrong parameters file type: {}".format(ext[1]))
elif type(params) is dict:
return PresetLoaderDict(params)
else:
raise Exception("wrong parameters input, is neither a string nor a json file name: {}::{}".format(params, type(params)))
def __init__(self):
if 'Knows' not in self.params:
if 'knows' in self.params:
self.params['Knows'] = self.params['knows']
else:
self.params['Knows'] = {}
if 'Settings' not in self.params:
if 'settings' in self.params:
self.params['Settings'] = self.params['settings']
else:
self.params['Settings'] = {}
if 'Controller' not in self.params:
if 'controller' in self.params:
self.params['Controller'] = self.params['controller']
else:
self.params['Controller'] = {}
self.params['score'] = self.computeScore()
def load(self, player):
# update the parameters in the parameters classes: Knows, Settings
Knows.knowsDict[player] = Knows()
Settings.SettingsDict[player] = Settings()
Controller.ControllerDict[player] = Controller()
# Knows
for param in self.params['Knows']:
if isKnows(param) and hasattr(Knows, param):
setattr(Knows.knowsDict[player], param, SMBool( self.params['Knows'][param][0],
self.params['Knows'][param][1],
['{}'.format(param)]))
# Settings
## hard rooms
for hardRoom in ['X-Ray', 'Gauntlet']:
if hardRoom in self.params['Settings']:
Settings.SettingsDict[player].hardRooms[hardRoom] = Settings.hardRoomsPresets[hardRoom][self.params['Settings'][hardRoom]]
## bosses
for boss in ['Kraid', 'Phantoon', 'Draygon', 'Ridley', 'MotherBrain']:
if boss in self.params['Settings']:
Settings.SettingsDict[player].bossesDifficulty[boss] = Settings.bossesDifficultyPresets[boss][self.params['Settings'][boss]]
## hellruns
for hellRun in ['Ice', 'MainUpperNorfair', 'LowerNorfair']:
if hellRun in self.params['Settings']:
Settings.SettingsDict[player].hellRuns[hellRun] = Settings.hellRunPresets[hellRun][self.params['Settings'][hellRun]]
# Controller
for button in self.params['Controller']:
if isButton(button):
setattr(Controller.ControllerDict[player], button, self.params['Controller'][button])
def dump(self, fileName):
with open(fileName, 'w') as jsonFile:
json.dump(self.params, jsonFile)
def printToScreen(self):
print("self.params: {}".format(self.params))
print("loaded knows: ")
for knows in Knows.__dict__:
if isKnows(knows):
print("{}: {}".format(knows, Knows.__dict__[knows]))
print("loaded settings:")
for setting in Settings.__dict__:
if isSettings(setting):
print("{}: {}".format(setting, Settings.__dict__[setting]))
print("loaded controller:")
for button in Controller.__dict__:
if isButton(button):
print("{}: {}".format(button, Controller.__dict__[button]))
print("loaded score: {}".format(self.params['score']))
def computeScore(self):
# the more techniques you know and the smaller the difficulty of the techniques, the higher the score
diff2score = {
easy: 6,
medium: 5,
hard: 4,
harder: 3,
hardcore: 2,
mania: 1
}
boss2score = {
"He's annoying": 1,
'A lot of trouble': 1,
"I'm scared!": 1,
"It can get ugly": 1,
'Default': 2,
'Quick Kill': 3,
'Used to it': 3,
'Is this really the last boss?': 3,
'No problemo': 4,
'Piece of cake': 4,
'Nice cutscene bro': 4
}
hellrun2score = {
'No thanks': 0,
'Solution': 0,
'Gimme energy': 2,
'Default': 4,
'Bring the heat': 6,
'I run RBO': 8
}
hellrunLN2score = {
'Default': 0,
'Solution': 0,
'Bring the heat': 6,
'I run RBO': 12
}
xray2score = {
'Aarghh': 0,
'Solution': 0,
"I don't like spikes": 1,
'Default': 2,
"I don't mind spikes": 3,
'D-Boost master': 4
}
gauntlet2score = {
'Aarghh': 0,
"I don't like acid": 1,
'Default': 2
}
score = 0
# knows
for know in Knows.__dict__:
if isKnows(know):
if know in self.params['Knows']:
if self.params['Knows'][know][0] == True:
score += diff2score[self.params['Knows'][know][1]]
else:
# if old preset with not all the knows, use default values for the know
if Knows.__dict__[know].bool == True:
score += diff2score[Knows.__dict__[know].difficulty]
# hard rooms
hardRoom = 'X-Ray'
if hardRoom in self.params['Settings']:
score += xray2score[self.params['Settings'][hardRoom]]
hardRoom = 'Gauntlet'
if hardRoom in self.params['Settings']:
score += gauntlet2score[self.params['Settings'][hardRoom]]
# bosses
for boss in ['Kraid', 'Phantoon', 'Draygon', 'Ridley', 'MotherBrain']:
if boss in self.params['Settings']:
score += boss2score[self.params['Settings'][boss]]
# hellruns
for hellRun in ['Ice', 'MainUpperNorfair']:
if hellRun in self.params['Settings']:
score += hellrun2score[self.params['Settings'][hellRun]]
hellRun = 'LowerNorfair'
if hellRun in self.params['Settings']:
score += hellrunLN2score[self.params['Settings'][hellRun]]
return score
class PresetLoaderJson(PresetLoader):
# when called from the test suite
def __init__(self, jsonFileName):
with open(jsonFileName) as jsonFile:
self.params = json.load(jsonFile)
super(PresetLoaderJson, self).__init__()
class PresetLoaderDict(PresetLoader):
# when called from the website
def __init__(self, params):
self.params = params
super(PresetLoaderDict, self).__init__()
def getDefaultMultiValues():
from graph.graph_utils import GraphUtils
defaultMultiValues = {
'startLocation': GraphUtils.getStartAccessPointNames(),
'majorsSplit': ['Full', 'FullWithHUD', 'Major', 'Chozo', 'Scavenger'],
'progressionSpeed': ['slowest', 'slow', 'medium', 'fast', 'fastest', 'basic', 'VARIAble', 'speedrun'],
'progressionDifficulty': ['easier', 'normal', 'harder'],
'morphPlacement': ['early', 'normal'], #['early', 'late', 'normal'],
'energyQty': ['ultra sparse', 'sparse', 'medium', 'vanilla'],
'gravityBehaviour': ['Vanilla', 'Balanced', 'Progressive']
}
return defaultMultiValues
def getPresetValues():
return [
"newbie",
"casual",
"regular",
"veteran",
"expert",
"master",
"samus",
"Season_Races",
"SMRAT2021",
"solution",
"custom",
"varia_custom"
]
# from web to cli
def convertParam(randoParams, param, inverse=False):
value = randoParams.get(param, "off" if inverse == False else "on")
if value == "on":
return True if inverse == False else False
elif value == "off":
return False if inverse == False else True
elif value == "random":
return "random"
raise Exception("invalid value for parameter {}".format(param))
def loadRandoPreset(world, player, args):
defaultMultiValues = getDefaultMultiValues()
diffs = ["easy", "medium", "hard", "harder", "hardcore", "mania", "infinity"]
presetValues = getPresetValues()
args.animals = world.animals[player].value
args.noVariaTweaks = not world.varia_tweaks[player].value
args.maxDifficulty = diffs[world.max_difficulty[player].value]
args.suitsRestriction = world.suits_restriction[player].value
#args.hideItems = world.hide_items[player].value
args.strictMinors = world.strict_minors[player].value
args.noLayout = not world.layout_patches[player].value
args.gravityBehaviour = defaultMultiValues["gravityBehaviour"][world.gravity_behaviour[player].value]
args.nerfedCharge = world.nerfed_charge[player].value
args.area = world.area_randomization[player].value != 0
if args.area:
args.areaLayoutBase = not world.area_layout[player].value
args.lightArea = world.area_randomization[player].value == 1
#args.escapeRando
#args.noRemoveEscapeEnemies
args.doorsColorsRando = world.doors_colors_rando[player].value
args.allowGreyDoors = world.allow_grey_doors[player].value
args.bosses = world.boss_randomization[player].value
if world.fun_combat[player].value:
args.superFun.append("Combat")
if world.fun_movement[player].value:
args.superFun.append("Movement")
if world.fun_suits[player].value:
args.superFun.append("Suits")
ipsPatches = {"spin_jump_restart":"spinjumprestart", "rando_speed":"rando_speed", "elevators_doors_speed":"elevators_doors_speed", "refill_before_save":"refill_before_save"}
for settingName, patchName in ipsPatches.items():
if hasattr(world, settingName) and getattr(world, settingName)[player].value:
args.patches.append(patchName + '.ips')
patches = {"no_music":"No_Music", "infinite_space_jump":"Infinite_Space_Jump"}
for settingName, patchName in patches.items():
if hasattr(world, settingName) and getattr(world, settingName)[player].value:
args.patches.append(patchName)
args.hud = world.hud[player].value
args.morphPlacement = defaultMultiValues["morphPlacement"][world.morph_placement[player].value]
#args.majorsSplit
#args.scavNumLocs
#args.scavRandomized
#args.scavEscape
args.startLocation = defaultMultiValues["startLocation"][world.start_location[player].value]
#args.progressionDifficulty
#args.progressionSpeed
args.missileQty = world.missile_qty[player].value / float(10)
args.superQty = world.super_qty[player].value / float(10)
args.powerBombQty = world.power_bomb_qty[player].value / float(10)
args.minorQty = world.minor_qty[player].value
args.energyQty = defaultMultiValues["energyQty"][world.energy_qty[player].value]
#args.minimizerN
#args.minimizerTourian
return presetValues[world.preset[player].value]
def getRandomizerDefaultParameters():
defaultParams = {}
defaultMultiValues = getDefaultMultiValues()
defaultParams['complexity'] = "simple"
defaultParams['preset'] = 'regular'
defaultParams['randoPreset'] = ""
defaultParams['raceMode'] = "off"
defaultParams['majorsSplit'] = "Full"
defaultParams['majorsSplitMultiSelect'] = defaultMultiValues['majorsSplit']
defaultParams['scavNumLocs'] = "10"
defaultParams['scavRandomized'] = "off"
defaultParams['scavEscape'] = "off"
defaultParams['startLocation'] = "Landing Site"
defaultParams['startLocationMultiSelect'] = defaultMultiValues['startLocation']
defaultParams['maxDifficulty'] = 'hardcore'
defaultParams['progressionSpeed'] = "medium"
defaultParams['progressionSpeedMultiSelect'] = defaultMultiValues['progressionSpeed']
defaultParams['progressionDifficulty'] = 'normal'
defaultParams['progressionDifficultyMultiSelect'] = defaultMultiValues['progressionDifficulty']
defaultParams['morphPlacement'] = "early"
defaultParams['morphPlacementMultiSelect'] = defaultMultiValues['morphPlacement']
defaultParams['suitsRestriction'] = "on"
defaultParams['hideItems'] = "off"
defaultParams['strictMinors'] = "off"
defaultParams['missileQty'] = "3"
defaultParams['superQty'] = "2"
defaultParams['powerBombQty'] = "1"
defaultParams['minorQty'] = "100"
defaultParams['energyQty'] = "vanilla"
defaultParams['energyQtyMultiSelect'] = defaultMultiValues['energyQty']
defaultParams['areaRandomization'] = "off"
defaultParams['areaLayout'] = "off"
defaultParams['lightAreaRandomization'] = "off"
defaultParams['doorsColorsRando'] = "off"
defaultParams['allowGreyDoors'] = "off"
defaultParams['escapeRando'] = "off"
defaultParams['removeEscapeEnemies'] = "off"
defaultParams['bossRandomization'] = "off"
defaultParams['minimizer'] = "off"
defaultParams['minimizerQty'] = "45"
defaultParams['minimizerTourian'] = "off"
defaultParams['funCombat'] = "off"
defaultParams['funMovement'] = "off"
defaultParams['funSuits'] = "off"
defaultParams['layoutPatches'] = "on"
defaultParams['variaTweaks'] = "on"
defaultParams['gravityBehaviour'] = "Balanced"
defaultParams['gravityBehaviourMultiSelect'] = defaultMultiValues['gravityBehaviour']
defaultParams['nerfedCharge'] = "off"
defaultParams['itemsounds'] = "on"
defaultParams['elevators_doors_speed'] = "on"
defaultParams['spinjumprestart'] = "off"
defaultParams['rando_speed'] = "off"
defaultParams['Infinite_Space_Jump'] = "off"
defaultParams['refill_before_save'] = "off"
defaultParams['hud'] = "off"
defaultParams['animals'] = "off"
defaultParams['No_Music'] = "off"
defaultParams['random_music'] = "off"
return defaultParams
def fixEnergy(items):
# display number of energy used
energies = [i for i in items if i.find('ETank') != -1]
if len(energies) > 0:
(maxETank, maxReserve, maxEnergy) = (0, 0, 0)
for energy in energies:
nETank = int(energy[0:energy.find('-ETank')])
if energy.find('-Reserve') != -1:
nReserve = int(energy[energy.find(' - ')+len(' - '):energy.find('-Reserve')])
else:
nReserve = 0
nEnergy = nETank + nReserve
if nEnergy > maxEnergy:
maxEnergy = nEnergy
maxETank = nETank
maxReserve = nReserve
items.remove(energy)
items.append('{}-ETank'.format(maxETank))
if maxReserve > 0:
items.append('{}-Reserve'.format(maxReserve))
return items

View File

@@ -0,0 +1,27 @@
import json, os.path
# record solver/rando to play in the VCR tracker
class VCR(object):
def __init__(self, name, type):
self.baseName = os.path.basename(os.path.splitext(name)[0])
self.outFileName = "{}.{}.vcr".format(self.baseName, type)
self.empty()
def empty(self):
self.tape = []
def addLocation(self, locName, itemName):
self.tape.append({'type': 'location', 'loc': locName, 'item': itemName})
def addRollback(self, count):
self.tape.append({'type': 'rollback', 'count': count})
def dump(self):
with open(self.outFileName, 'w') as jsonFile:
json.dump(self.tape, jsonFile)
# in scavenger we have the rando solver then the scav solver, generate vcr for both
def reinit(self, type):
self.dump()
self.outFileName = "{}.{}.vcr".format(self.baseName, type)
self.empty()

View File

@@ -0,0 +1,3 @@
# version displayed on the title screen, must be a max 32 chars [a-z0-9.-] string
# either 'beta' or 'r.yyyy.mm.dd'
displayedVersion = 'beta'