forked from mirror/Archipelago
* Add the world * doc update * docs * Fix Blast/Missile not clearing Reflect * Update worlds/earthbound/__init__.py Co-authored-by: Duck <31627079+duckboycool@users.noreply.github.com> * Update worlds/earthbound/__init__.py remove unused import Co-authored-by: Duck <31627079+duckboycool@users.noreply.github.com> * Update worlds/earthbound/__init__.py Co-authored-by: Duck <31627079+duckboycool@users.noreply.github.com> * Update worlds/earthbound/modules/dungeon_er.py make bool optional Co-authored-by: Duck <31627079+duckboycool@users.noreply.github.com> * Update worlds/earthbound/modules/boss_shuffle.py typing update Co-authored-by: Duck <31627079+duckboycool@users.noreply.github.com> * Update worlds/earthbound/modules/boss_shuffle.py Co-authored-by: Duck <31627079+duckboycool@users.noreply.github.com> * Filter events out of item name to id * we call it a glorp * Update worlds/earthbound/Regions.py Co-authored-by: Duck <31627079+duckboycool@users.noreply.github.com> * Update worlds/earthbound/__init__.py Co-authored-by: Duck <31627079+duckboycool@users.noreply.github.com> * Update worlds/earthbound/Items.py Co-authored-by: Duck <31627079+duckboycool@users.noreply.github.com> * Update worlds/earthbound/Regions.py Co-authored-by: Duck <31627079+duckboycool@users.noreply.github.com> * Fix missing optional import * hint stuff * -Fix Apple Kid text being wrong -Fix Slimy Pile text being wrong * -Fix some sprite corruption if PSI was used when an enemy loaded another enemy -Fixed a visible artifact tile during some cutscenes * Update ver * Update docs * Fix some money scripting issues * Add argument to PSI fakeout attack * Updated monkey caves shop description * Remove closing markdown from doc * Add new flavors * Make flavors actually work * Update platforms * Fix common gear getting duplicated * Split region initialization * Condense checks for start inventory + some other junk * Fix some item groups - change receiver phone to warp pad * wow that one was really bad :glorp: * blah * Fix cutoff option text * switch start inventory concatenation to itertools * Fix sky runner scripting bug - added some new comm suggestions * Fix crash when generating with spoiler_only * Fix happy-happy teleport not unlocking after beating carpainter * Hint man hints can now use CreateHint packets to create hints in other games * Adjust some filler rarity * Update world to use CreateHints and deprecate old method * Fix epilogue skip being offset * Rearrange a couple regions * Fix tendapants getting deleted in battle * update doc * i got scared and forgot i had multiple none checks and am worried about this triggering but tested and it works * Fix mostly typing errors from silvris * More type checks * More typing * Typema * Type * Fix enemy levels overwriting music * Fix gihugic blunder * Fix Lumine Hall enabling OSS * del world * Rel 4.2.7 * Remove some debug logs * Fix vanilla bug with weird ambush detection * Fix Starman Junior having an unscaled Freeze * Change shop scaling * Fix shops using the wrong thankful script * Update some bosses in boss shuffle * Loc group adjustment * Update some boss shuffle stuff | Fix Enemizer attacks getting overwritten by Shuffle data | Fix flunkies not updating and still being used with enemizer * Get rid of some debug stuff * Get boss shuffle running, dont merge * Fix json and get boss shuffle no plando back up * Fix Magicant Boost not initializing to Ness if party count = 4 * Fix belch shop using wrong logic * Don't re-send goal status * EBitem * remove : * idk if this is whatvi wanted * All client messagesnow only send when relevant instead of constantly * Patch up the rest of boss plando * Fix Giygas being not excluded from enemizer * Fix epilogue again * adjust the sphere scaling name * add the things * Fix Ness being placed onto monotoli when monotoli was in sea of eden * Fix prefill properly * Fix boss shuffle on vanilla slots. * rename this, apparently * Update archipelago.json --------- Co-authored-by: Duck <31627079+duckboycool@users.noreply.github.com> Co-authored-by: NewSoupVi <57900059+NewSoupVi@users.noreply.github.com>
144 lines
5.9 KiB
Python
144 lines
5.9 KiB
Python
import struct
|
|
from .enemy_attributes import (enemy_species, enemy_adjectives, battle_sprites, field_sprites, excluded_enemies,
|
|
insects, robots, movement_patterns, start_texts, death_texts, weakness_table)
|
|
from ...game_data.text_data import calc_pixel_width, text_encoder
|
|
from typing import TYPE_CHECKING
|
|
if TYPE_CHECKING:
|
|
from ... import EarthBoundWorld
|
|
from ...Rom import LocalRom
|
|
shield_statuses = [
|
|
"phys_1",
|
|
"phys_2",
|
|
"psi_1",
|
|
"psi_2"
|
|
]
|
|
|
|
battle_songs = [
|
|
0x60,
|
|
0x61,
|
|
0x62,
|
|
0x63,
|
|
0x64,
|
|
0x65,
|
|
0x66,
|
|
0x67,
|
|
0x68,
|
|
0x69,
|
|
0x8D,
|
|
0x94
|
|
]
|
|
|
|
|
|
def randomize_enemy_attributes(world: "EarthBoundWorld", rom: "LocalRom") -> None:
|
|
"""Randomizes various attributes of enemies. This includes the name,
|
|
gender, sprite, color, etc. Data can be found in enemy_attributes."""
|
|
taken_names = []
|
|
for enemy in world.enemies:
|
|
if enemy not in excluded_enemies and " (" not in enemy:
|
|
new_name = "FFFFFFFFFFFFFFFFFFFFFFFFFF"
|
|
pixel_width = calc_pixel_width(new_name)
|
|
species = "Null"
|
|
while not (len(new_name) <= 25 and new_name not in taken_names and pixel_width <= 95):
|
|
species = world.random.choice(enemy_species)
|
|
adjective = world.random.choice(enemy_adjectives)
|
|
new_name = adjective + species
|
|
pixel_width = calc_pixel_width(new_name)
|
|
taken_names.append(new_name)
|
|
sprite = world.random.choice(battle_sprites[species])
|
|
field_sprite = field_sprites[sprite]
|
|
world.enemy_sprites[enemy] = field_sprite
|
|
movement_pattern = movement_patterns[field_sprite]
|
|
palette = world.random.randint(1, 31)
|
|
gender = world.random.randint(1, 3)
|
|
if species in robots:
|
|
enemy_type = 2
|
|
elif species in insects:
|
|
enemy_type = 1
|
|
else:
|
|
enemy_type = 0
|
|
row = world.random.randint(0, 1)
|
|
mirror_chance = world.random.randint(0, 100)
|
|
start_text = world.random.choice(start_texts)
|
|
death_text = world.random.choice(death_texts)
|
|
if species in ["Power Robot", "Reactor Robot", "Sphere"]:
|
|
death_action = 0x0040
|
|
elif species == "Oak":
|
|
death_action = 0x0041
|
|
else:
|
|
death_action = 0x0000
|
|
music = world.random.choice(battle_songs)
|
|
drop_rate = world.random.randint(0, 7)
|
|
base_drop = world.random.choice(world.filler_drops)
|
|
if world.random.randint(1, 100) < 6:
|
|
status = world.random.randint(1, 7)
|
|
if status < 5:
|
|
world.enemies[enemy].has_shield = shield_statuses[status - 1]
|
|
else:
|
|
status = 0
|
|
|
|
if species in ["Party Man", "Reveler"]:
|
|
miss_rate = 6
|
|
elif species == "Boy":
|
|
miss_rate = 8
|
|
elif species == "Bot":
|
|
miss_rate = 5
|
|
else:
|
|
miss_rate = world.random.randint(0, 4)
|
|
|
|
fire_weakness = get_weakness("Fire", species)
|
|
|
|
freeze_weakness = get_weakness("Freeze", species)
|
|
|
|
flash_weakness = get_weakness("Flash", species)
|
|
|
|
paralysis_weakness = get_weakness("Paralysis", species)
|
|
|
|
hypnosis_weakness = get_weakness("Hypnosis", species)
|
|
|
|
address = world.enemies[enemy].address
|
|
new_name = text_encoder(new_name, 0x18)
|
|
if len(new_name) < 0x18:
|
|
new_name.extend([0x00])
|
|
|
|
if world.enemies[enemy].attack_extensions > 1:
|
|
num_enemies = world.enemies[enemy].attack_extensions
|
|
else:
|
|
num_enemies = 1
|
|
|
|
for i in range(num_enemies):
|
|
rom.write_bytes(address, bytearray([0x01]))
|
|
rom.write_bytes(address + 1, new_name)
|
|
rom.write_bytes(address + 0x1A, bytearray([gender]))
|
|
rom.write_bytes(address + 0x1B, bytearray([enemy_type]))
|
|
rom.write_bytes(address + 0x1C, struct.pack("H", sprite))
|
|
rom.write_bytes(address + 0x1E, struct.pack("H", field_sprite))
|
|
rom.write_bytes(address + 0x2B, struct.pack("H", movement_pattern))
|
|
rom.write_bytes(address + 0x2D, struct.pack("I", start_text))
|
|
rom.write_bytes(address + 0x31, struct.pack("I", death_text))
|
|
rom.write_bytes(address + 0x35, bytearray([palette]))
|
|
rom.write_bytes(address + 0x37, bytearray([music]))
|
|
rom.write_bytes(address + 0x3F, bytearray([fire_weakness]))
|
|
rom.write_bytes(address + 0x40, bytearray([freeze_weakness]))
|
|
rom.write_bytes(address + 0x41, bytearray([flash_weakness]))
|
|
rom.write_bytes(address + 0x42, bytearray([paralysis_weakness]))
|
|
rom.write_bytes(address + 0x43, bytearray([hypnosis_weakness]))
|
|
rom.write_bytes(address + 0x43, bytearray([miss_rate]))
|
|
rom.write_bytes(address + 0x4E, struct.pack("H", death_action))
|
|
rom.write_bytes(address + 0x57, bytearray([drop_rate]))
|
|
rom.write_bytes(address + 0x58, bytearray([base_drop]))
|
|
rom.write_bytes(address + 0x59, bytearray([status]))
|
|
rom.write_bytes(address + 0x5B, bytearray([row]))
|
|
rom.write_bytes(address + 0x5D, bytearray([mirror_chance]))
|
|
if world.enemies[enemy].attack_extensions > 1:
|
|
address = world.enemies[f"{enemy} ({i + 2})"].address
|
|
enemy = f"{enemy} ({i + 2})"
|
|
|
|
|
|
def get_weakness(element: str, species: str) -> int:
|
|
"""Returns a weakness to given element, given the enemy's base species."""
|
|
if species in weakness_table[element]:
|
|
weakness = weakness_table[element][species]
|
|
else:
|
|
weakness = 1
|
|
return weakness
|