Skip to content

Commit

Permalink
add monstermash code
Browse files Browse the repository at this point in the history
  • Loading branch information
Anonymous committed Apr 7, 2023
1 parent 4a2e201 commit a449513
Showing 1 changed file with 193 additions and 14 deletions.
207 changes: 193 additions & 14 deletions randomizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ def hexify(s):
return '-'.join('{0:0>2X}'.format(c) for c in s)


def lange(low, high):
return list(range(low, high))


EVENT_PATCHES = [
'skip_tutorial',
'treadool_warp',
Expand Down Expand Up @@ -417,6 +421,21 @@ def monsters(self):
return [MonsterObject.get(index) if index < 0xFF else None
for index in self.monster_indexes]

@cached_property
def clean_monsters(self):
return [m for m in self.monsters if m is not None]

@property
def rank(self):
if self.monsters[0] is None:
return -1
return self.monsters[0].rank

def guess_sprite(self):
if self.monsters[0] is None:
return None
return self.monsters[0].guess_sprite()

def preprocess(self):
self.monsters

Expand Down Expand Up @@ -3734,6 +3753,22 @@ def zone_index(self):
return self.reverse_zone_map[self.index]
return None

def get_zone_enemies(self, old=True):
sprite_formation_matcher = re.compile(
'#.*PRELOAD (..) .* \(FORMATION (..):')
result = []
for meo in self.neighbors:
if old:
s = meo.old_pretty
else:
s = str(meo)
sprite_formations = sprite_formation_matcher.findall(s)
for sprite, formation in sprite_formations:
sprite = int(sprite, 0x10)
formation = FormationObject.get(int(formation, 0x10))
result.append((sprite, formation))
return result

@property
def neighbors(self):
if self.zone_index is None:
Expand Down Expand Up @@ -5690,16 +5725,8 @@ def extract_location_boss(findstr):
f.write(data)


def scale_enemies(location_ranks, boss_events,
normal_scale_weight=0.75, boss_scale_weight=0.75):
if scalecustom_nonboss is not None:
normal_scale_weight = scalecustom_nonboss
if scalecustom_boss is not None:
boss_scale_weight = scalecustom_boss
MapEventObject.class_reseed('scale_enemies')
random.shuffle(boss_events)
def get_ranked_locations(location_ranks, randoms_only=False):
ranked_locations = []
ranked_bosses = []
for i in sorted(location_ranks):
locations = sorted(location_ranks[i])
random.shuffle(locations)
Expand All @@ -5709,9 +5736,6 @@ def scale_enemies(location_ranks, boss_events,
loc_properties = OpenNPCGenerator.get_properties_by_name(loc)
if loc_properties is None:
continue
for b in boss_events:
if b.startswith(loc_properties.map_index.upper()):
ranked_bosses.append(b)
map_index = int(loc_properties.map_index, 0x10)
meo = MapEventObject.get(map_index)
if meo.zone_name not in ranked_locations:
Expand All @@ -5724,10 +5748,158 @@ def scale_enemies(location_ranks, boss_events,
if meo.zone_name in temp:
continue
s = str(meo)
if '(FORMATION' in s or 'Invoke Battle' in s:
if '(FORMATION' in s:
temp.append(meo.zone_name)
elif 'Invoke Battle' in s and not randoms_only:
temp.append(meo.zone_name)
ranked_locations = [loc for loc in ranked_locations if loc in temp]

return ranked_locations


def replace_map_formations(location_ranks=None):
FormationObject.class_reseed('replace_map_formations')
JELLY_SPRITE = 0x94
SPRITE_OVERRIDES = {FormationObject.get(0): {0x36}}

big_sprites = set(lange(0xb6, 0xbe) + lange(0xbf, 0xc5)
+ lange(0xeb, 0xf0))
small_sprites = {s for s in range(0x80, 0xf0) if s not in big_sprites}
all_formation_sprites = defaultdict(set)

if location_ranks is not None:
ranked_locations = get_ranked_locations(location_ranks,
randoms_only=True)
else:
ranked_locations = None

for zone_index in sorted(MapEventObject.zone_names):
meo = MapEventObject.get(zone_index)
assert meo.zone_index == meo.index == zone_index
zone_name = meo.zone_name
zone_sprite_formations = meo.get_zone_enemies()
for sprite, formation in zone_sprite_formations:
all_formation_sprites[formation].add(sprite)

for f in FormationObject.every:
sprite = f.guess_sprite()
if sprite is not None:
all_formation_sprites[f].add(sprite)

for f in SPRITE_OVERRIDES:
all_formation_sprites[f] = SPRITE_OVERRIDES[f]

cores = {0xa7, 0xa8, 0xa9, 0xaa}
ranked_formations = [f for f in FormationObject.ranked if f.rank >= 0
and f in all_formation_sprites
and JELLY_SPRITE not in all_formation_sprites[f]
and not set(f.monster_indexes) & cores]
small_formations = [f for f in ranked_formations
if all_formation_sprites[f] & small_sprites]
big_formations = [f for f in ranked_formations
if all_formation_sprites[f] & big_sprites]
for zone_index in sorted(MapEventObject.zone_names):
meo = MapEventObject.get(zone_index)
assert meo.zone_index == meo.index == zone_index
zone_name = meo.zone_name
if ranked_locations is not None:
if zone_name not in ranked_locations:
continue
rank = ranked_locations.index(zone_name) / len(ranked_locations)
else:
rank = None

zone_sprite_formations = meo.get_zone_enemies()
sprites = {s for (s, f) in zone_sprite_formations if s != JELLY_SPRITE}
formations = {f for (s, f) in zone_sprite_formations}
my_small = {s for s in sprites if s in small_sprites}
my_big = {s for s in sprites if s in big_sprites}

new_sprite_map = {}
new_formation_map = {}
for spriteset in (my_small, my_big):
if spriteset is my_small:
candidate_formations = small_formations
else:
candidate_formations = big_formations
for s in sorted(spriteset):
candidate_formations = [
f for f in candidate_formations if not
all_formation_sprites[f] & set(new_sprite_map.values())]

if rank is None:
old_formations = [f for f in sorted(formations)
if s in all_formation_sprites[f]]
if not old_formations:
continue
base = random.choice(old_formations)
new_formation = base.get_similar(
candidates=candidate_formations,
override_outsider=True)
else:
shuffled = shuffle_simple(
candidate_formations,
random_degree=FormationObject.random_degree)
max_index = len(shuffled)-1
index = int(round(rank * max_index))
new_formation = shuffled[index]

new_sprites = all_formation_sprites[new_formation]
if len(new_sprites) > 1:
new_sprite = random.choice(sorted(new_sprites))
else:
new_sprite = list(new_sprites)[0]

assert s not in new_sprite_map
new_sprite_map[s] = new_sprite
assert new_sprite not in new_formation_map
similar_formations = [
f for f in candidate_formations
if new_sprite in all_formation_sprites[f] and
set(f.clean_monsters) & set(new_formation.clean_monsters)]
assert new_formation in similar_formations
new_formation_map[new_sprite] = similar_formations

for neighbor in meo.neighbors:
mfo = MapFormationsObject.get(neighbor.index)
signature = '{0:0>2X}-X-XX'.format(neighbor.index)
script = neighbor.get_script_by_signature(signature)
new_script = []
for (l, o, p) in script.script:
if o in {0x68, 0x7B}:
npc_event_index, sprite = p
if sprite in new_sprite_map:
new_sprite = new_sprite_map[sprite]
if o == 0x7B:
new_formation = random.choice(
new_formation_map[new_sprite])
formation_index = npc_event_index-0x50
assert formation_index >= 0
new_index = new_formation.index
mfo.formation_indexes[formation_index] = new_index
p = [npc_event_index, new_sprite]
new_script.append((l, o, p))
script.script = new_script


def scale_enemies(location_ranks, boss_events,
normal_scale_weight=0.75, boss_scale_weight=0.75):
if scalecustom_nonboss is not None:
normal_scale_weight = scalecustom_nonboss
if scalecustom_boss is not None:
boss_scale_weight = scalecustom_boss
MapEventObject.class_reseed('scale_enemies')
ranked_locations = get_ranked_locations(location_ranks)

random.shuffle(boss_events)
ranked_bosses = []
for i, signature in enumerate(boss_events):
map_index, _, _ = signature.split('-')
map_index = int(map_index, 0x10)
zone_name = MapEventObject.get(map_index).zone_name
ranked_bosses.append((ranked_locations.index(zone_name), i, signature))
ranked_bosses = [b for (l, i, b) in sorted(ranked_bosses)]

formation_matcher = re.compile('#.*\(FORMATION (..): ')
monster_ranks = defaultdict(list)
for meo in MapEventObject.every:
Expand Down Expand Up @@ -6067,6 +6239,9 @@ def make_open_world(custom=None):
if location not in OpenNPCGenerator.done_locations:
OpenNPCGenerator.create_crown(location)

if 'monstermash' in get_activated_codes():
replace_map_formations(ir.location_ranks)

if (('scale' in get_activated_codes() or 'm' in get_flags()) and
'noscale' not in get_activated_codes()):
scale_enemies(ir.location_ranks, boss_events)
Expand Down Expand Up @@ -6161,7 +6336,8 @@ class VanillaObject(TableObject):
'splitscale': ['splitscale'],
'scale': ['scale'],
'noscale': ['noscale'],
'bossy': ['bossy']
'bossy': ['bossy'],
'monstermash': ['monstermash'],
}
run_interface(ALL_OBJECTS, snes=True, codes=codes,
custom_degree=True, custom_difficulty=True)
Expand Down Expand Up @@ -6200,6 +6376,9 @@ class VanillaObject(TableObject):
else:
custom = None
make_open_world(custom=custom)
else:
if 'monstermash' in get_activated_codes():
replace_map_formations()

clean_and_write(ALL_OBJECTS)
dump_events('_l2r_event_dump.txt')
Expand Down

0 comments on commit a449513

Please sign in to comment.