Pokémon R/B: Version 3 (#1520)
* Coin items received or found in the Game Corner are now shuffled, locations require Coin Case * Prizesanity option (shuffle Game Corner Prizes) * DexSanity option: location checks for marking Pokémon as caught in your Pokédex. Also an option to set all Pokémon in your Pokédex as seen from the start, to aid in locating them. * Option to randomize the layout of the Rock Tunnel. * Area 1-to-1 mapping: When one instance of a Wild Pokémon in a given area is randomized, all instances of that Pokémon will be the same. So that if a route had 3 different Pokémon before, it will have 3 after randomization. * Option to randomize the moves taught by TMs. * Exact controls for TM/HM compatibility chances. * Option to randomize Pokémon's pallets or set them based on primary type. * Added Cinnabar Gym trainers to Trainersanity and randomized the quiz questions and answers. Getting a correct answer will flag the trainer as defeated so that you can obtain the Trainersanity check without defeating the trainer if you answer correctly.
This commit is contained in:
parent
4d7bd929bc
commit
df55455fc0
|
@ -17,7 +17,7 @@ from CommonClient import CommonContext, server_loop, gui_enabled, ClientCommandP
|
|||
from worlds.pokemon_rb.locations import location_data
|
||||
from worlds.pokemon_rb.rom import RedDeltaPatch, BlueDeltaPatch
|
||||
|
||||
location_map = {"Rod": {}, "EventFlag": {}, "Missable": {}, "Hidden": {}, "list": {}}
|
||||
location_map = {"Rod": {}, "EventFlag": {}, "Missable": {}, "Hidden": {}, "list": {}, "DexSanityFlag": {}}
|
||||
location_bytes_bits = {}
|
||||
for location in location_data:
|
||||
if location.ram_address is not None:
|
||||
|
@ -40,7 +40,7 @@ CONNECTION_INITIAL_STATUS = "Connection has not been initiated"
|
|||
|
||||
DISPLAY_MSGS = True
|
||||
|
||||
SCRIPT_VERSION = 1
|
||||
SCRIPT_VERSION = 3
|
||||
|
||||
|
||||
class GBCommandProcessor(ClientCommandProcessor):
|
||||
|
@ -70,6 +70,8 @@ class GBContext(CommonContext):
|
|||
self.set_deathlink = False
|
||||
self.client_compatibility_mode = 0
|
||||
self.items_handling = 0b001
|
||||
self.sent_release = False
|
||||
self.sent_collect = False
|
||||
|
||||
async def server_auth(self, password_requested: bool = False):
|
||||
if password_requested and not self.password:
|
||||
|
@ -124,7 +126,8 @@ def get_payload(ctx: GBContext):
|
|||
"items": [item.item for item in ctx.items_received],
|
||||
"messages": {f'{key[0]}:{key[1]}': value for key, value in ctx.messages.items()
|
||||
if key[0] > current_time - 10},
|
||||
"deathlink": ctx.deathlink_pending
|
||||
"deathlink": ctx.deathlink_pending,
|
||||
"options": ((ctx.permissions['release'] in ('goal', 'enabled')) * 2) + (ctx.permissions['collect'] in ('goal', 'enabled'))
|
||||
}
|
||||
)
|
||||
ctx.deathlink_pending = False
|
||||
|
@ -134,10 +137,13 @@ def get_payload(ctx: GBContext):
|
|||
async def parse_locations(data: List, ctx: GBContext):
|
||||
locations = []
|
||||
flags = {"EventFlag": data[:0x140], "Missable": data[0x140:0x140 + 0x20],
|
||||
"Hidden": data[0x140 + 0x20: 0x140 + 0x20 + 0x0E], "Rod": data[0x140 + 0x20 + 0x0E:]}
|
||||
"Hidden": data[0x140 + 0x20: 0x140 + 0x20 + 0x0E],
|
||||
"Rod": data[0x140 + 0x20 + 0x0E:0x140 + 0x20 + 0x0E + 0x01]}
|
||||
|
||||
if len(flags['Rod']) > 1:
|
||||
return
|
||||
if len(data) > 0x140 + 0x20 + 0x0E + 0x01:
|
||||
flags["DexSanityFlag"] = data[0x140 + 0x20 + 0x0E + 0x01:]
|
||||
else:
|
||||
flags["DexSanityFlag"] = [0] * 19
|
||||
|
||||
for flag_type, loc_map in location_map.items():
|
||||
for flag, loc_id in loc_map.items():
|
||||
|
@ -207,6 +213,16 @@ async def gb_sync_task(ctx: GBContext):
|
|||
async_start(parse_locations(data_decoded['locations'], ctx))
|
||||
if 'deathLink' in data_decoded and data_decoded['deathLink'] and 'DeathLink' in ctx.tags:
|
||||
await ctx.send_death(ctx.auth + " is out of usable Pokémon! " + ctx.auth + " blacked out!")
|
||||
if 'options' in data_decoded:
|
||||
msgs = []
|
||||
if data_decoded['options'] & 4 and not ctx.sent_release:
|
||||
ctx.sent_release = True
|
||||
msgs.append({"cmd": "Say", "text": "!release"})
|
||||
if data_decoded['options'] & 8 and not ctx.sent_collect:
|
||||
ctx.sent_collect = True
|
||||
msgs.append({"cmd": "Say", "text": "!collect"})
|
||||
if msgs:
|
||||
await ctx.send_msgs(msgs)
|
||||
if ctx.set_deathlink:
|
||||
await ctx.update_death_link(True)
|
||||
except asyncio.TimeoutError:
|
||||
|
|
|
@ -7,7 +7,7 @@ local STATE_TENTATIVELY_CONNECTED = "Tentatively Connected"
|
|||
local STATE_INITIAL_CONNECTION_MADE = "Initial Connection Made"
|
||||
local STATE_UNINITIALIZED = "Uninitialized"
|
||||
|
||||
local SCRIPT_VERSION = 1
|
||||
local SCRIPT_VERSION = 3
|
||||
|
||||
local APIndex = 0x1A6E
|
||||
local APDeathLinkAddress = 0x00FD
|
||||
|
@ -16,7 +16,8 @@ local EventFlagAddress = 0x1735
|
|||
local MissableAddress = 0x161A
|
||||
local HiddenItemsAddress = 0x16DE
|
||||
local RodAddress = 0x1716
|
||||
local InGame = 0x1A71
|
||||
local DexSanityAddress = 0x1A71
|
||||
local InGameAddress = 0x1A84
|
||||
local ClientCompatibilityAddress = 0xFF00
|
||||
|
||||
local ItemsReceived = nil
|
||||
|
@ -34,6 +35,7 @@ local frame = 0
|
|||
local u8 = nil
|
||||
local wU8 = nil
|
||||
local u16
|
||||
local compat = nil
|
||||
|
||||
local function defineMemoryFunctions()
|
||||
local memDomain = {}
|
||||
|
@ -70,18 +72,6 @@ function slice (tbl, s, e)
|
|||
return new
|
||||
end
|
||||
|
||||
function processBlock(block)
|
||||
if block == nil then
|
||||
return
|
||||
end
|
||||
local itemsBlock = block["items"]
|
||||
memDomain.wram()
|
||||
if itemsBlock ~= nil then
|
||||
ItemsReceived = itemsBlock
|
||||
end
|
||||
deathlink_rec = block["deathlink"]
|
||||
end
|
||||
|
||||
function difference(a, b)
|
||||
local aa = {}
|
||||
for k,v in pairs(a) do aa[v]=true end
|
||||
|
@ -99,6 +89,7 @@ function generateLocationsChecked()
|
|||
events = uRange(EventFlagAddress, 0x140)
|
||||
missables = uRange(MissableAddress, 0x20)
|
||||
hiddenitems = uRange(HiddenItemsAddress, 0x0E)
|
||||
dexsanity = uRange(DexSanityAddress, 19)
|
||||
rod = u8(RodAddress)
|
||||
|
||||
data = {}
|
||||
|
@ -108,6 +99,9 @@ function generateLocationsChecked()
|
|||
table.foreach(hiddenitems, function(k, v) table.insert(data, v) end)
|
||||
table.insert(data, rod)
|
||||
|
||||
if compat > 1 then
|
||||
table.foreach(dexsanity, function(k, v) table.insert(data, v) end)
|
||||
end
|
||||
return data
|
||||
end
|
||||
|
||||
|
@ -141,7 +135,15 @@ function receive()
|
|||
return
|
||||
end
|
||||
if l ~= nil then
|
||||
processBlock(json.decode(l))
|
||||
block = json.decode(l)
|
||||
if block ~= nil then
|
||||
local itemsBlock = block["items"]
|
||||
if itemsBlock ~= nil then
|
||||
ItemsReceived = itemsBlock
|
||||
end
|
||||
deathlink_rec = block["deathlink"]
|
||||
|
||||
end
|
||||
end
|
||||
-- Determine Message to send back
|
||||
memDomain.rom()
|
||||
|
@ -156,15 +158,31 @@ function receive()
|
|||
seedName = newSeedName
|
||||
local retTable = {}
|
||||
retTable["scriptVersion"] = SCRIPT_VERSION
|
||||
retTable["clientCompatibilityVersion"] = u8(ClientCompatibilityAddress)
|
||||
|
||||
if compat == nil then
|
||||
compat = u8(ClientCompatibilityAddress)
|
||||
if compat < 2 then
|
||||
InGameAddress = 0x1A71
|
||||
end
|
||||
end
|
||||
|
||||
retTable["clientCompatibilityVersion"] = compat
|
||||
retTable["playerName"] = playerName
|
||||
retTable["seedName"] = seedName
|
||||
memDomain.wram()
|
||||
if u8(InGame) == 0xAC then
|
||||
|
||||
in_game = u8(InGameAddress)
|
||||
if in_game == 0x2A or in_game == 0xAC then
|
||||
retTable["locations"] = generateLocationsChecked()
|
||||
elseif in_game ~= 0 then
|
||||
print("Game may have crashed")
|
||||
curstate = STATE_UNINITIALIZED
|
||||
return
|
||||
end
|
||||
|
||||
retTable["deathLink"] = deathlink_send
|
||||
deathlink_send = false
|
||||
|
||||
msg = json.encode(retTable).."\n"
|
||||
local ret, error = gbSocket:send(msg)
|
||||
if ret == nil then
|
||||
|
@ -193,16 +211,23 @@ function main()
|
|||
if (curstate == STATE_OK) or (curstate == STATE_INITIAL_CONNECTION_MADE) or (curstate == STATE_TENTATIVELY_CONNECTED) then
|
||||
if (frame % 5 == 0) then
|
||||
receive()
|
||||
if u8(InGame) == 0xAC and u8(APItemAddress) == 0x00 then
|
||||
ItemIndex = u16(APIndex)
|
||||
if deathlink_rec == true then
|
||||
wU8(APDeathLinkAddress, 1)
|
||||
elseif u8(APDeathLinkAddress) == 3 then
|
||||
wU8(APDeathLinkAddress, 0)
|
||||
deathlink_send = true
|
||||
end
|
||||
if ItemsReceived[ItemIndex + 1] ~= nil then
|
||||
wU8(APItemAddress, ItemsReceived[ItemIndex + 1] - 172000000)
|
||||
in_game = u8(InGameAddress)
|
||||
if in_game == 0x2A or in_game == 0xAC then
|
||||
if u8(APItemAddress) == 0x00 then
|
||||
ItemIndex = u16(APIndex)
|
||||
if deathlink_rec == true then
|
||||
wU8(APDeathLinkAddress, 1)
|
||||
elseif u8(APDeathLinkAddress) == 3 then
|
||||
wU8(APDeathLinkAddress, 0)
|
||||
deathlink_send = true
|
||||
end
|
||||
if ItemsReceived[ItemIndex + 1] ~= nil then
|
||||
item_id = ItemsReceived[ItemIndex + 1] - 172000000
|
||||
if item_id > 255 then
|
||||
item_id = item_id - 256
|
||||
end
|
||||
wU8(APItemAddress, item_id)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -15,7 +15,7 @@ from .options import pokemon_rb_options
|
|||
from .rom_addresses import rom_addresses
|
||||
from .text import encode_text
|
||||
from .rom import generate_output, get_base_rom_bytes, get_base_rom_path, process_pokemon_data, process_wild_pokemon,\
|
||||
process_static_pokemon
|
||||
process_static_pokemon, process_move_data
|
||||
from .rules import set_rules
|
||||
|
||||
import worlds.pokemon_rb.poke_data as poke_data
|
||||
|
@ -40,13 +40,14 @@ class PokemonRedBlueWorld(World):
|
|||
game = "Pokemon Red and Blue"
|
||||
option_definitions = pokemon_rb_options
|
||||
|
||||
data_version = 5
|
||||
required_client_version = (0, 3, 7)
|
||||
data_version = 7
|
||||
required_client_version = (0, 3, 9)
|
||||
|
||||
topology_present = False
|
||||
|
||||
item_name_to_id = {name: data.id for name, data in item_table.items()}
|
||||
location_name_to_id = {location.name: location.address for location in location_data if location.type == "Item"}
|
||||
location_name_to_id = {location.name: location.address for location in location_data if location.type == "Item"
|
||||
and location.address is not None}
|
||||
item_name_groups = item_groups
|
||||
|
||||
web = PokemonWebWorld()
|
||||
|
@ -58,11 +59,14 @@ class PokemonRedBlueWorld(World):
|
|||
self.extra_badges = {}
|
||||
self.type_chart = None
|
||||
self.local_poke_data = None
|
||||
self.local_move_data = None
|
||||
self.local_tms = None
|
||||
self.learnsets = None
|
||||
self.trainer_name = None
|
||||
self.rival_name = None
|
||||
self.type_chart = None
|
||||
self.traps = None
|
||||
self.trade_mons = {}
|
||||
|
||||
@classmethod
|
||||
def stage_assert_generate(cls, multiworld: MultiWorld):
|
||||
|
@ -94,6 +98,12 @@ class PokemonRedBlueWorld(World):
|
|||
if len(self.multiworld.player_name[self.player].encode()) > 16:
|
||||
raise Exception(f"Player name too long for {self.multiworld.get_player_name(self.player)}. Player name cannot exceed 16 bytes for Pokémon Red and Blue.")
|
||||
|
||||
if (self.multiworld.dexsanity[self.player] and self.multiworld.accessibility[self.player] == "locations"
|
||||
and (self.multiworld.catch_em_all[self.player] != "all_pokemon"
|
||||
or self.multiworld.randomize_wild_pokemon[self.player] == "vanilla"
|
||||
or self.multiworld.randomize_legendary_pokemon[self.player] != "any")):
|
||||
self.multiworld.accessibility[self.player] = self.multiworld.accessibility[self.player].from_text("items")
|
||||
|
||||
if self.multiworld.badges_needed_for_hm_moves[self.player].value >= 2:
|
||||
badges_to_add = ["Marsh Badge", "Volcano Badge", "Earth Badge"]
|
||||
if self.multiworld.badges_needed_for_hm_moves[self.player].value == 3:
|
||||
|
@ -107,6 +117,7 @@ class PokemonRedBlueWorld(World):
|
|||
for badge in badges_to_add:
|
||||
self.extra_badges[hm_moves.pop()] = badge
|
||||
|
||||
process_move_data(self)
|
||||
process_pokemon_data(self)
|
||||
|
||||
if self.multiworld.randomize_type_chart[self.player] == "vanilla":
|
||||
|
@ -178,8 +189,13 @@ class PokemonRedBlueWorld(World):
|
|||
if self.multiworld.randomize_pokedex[self.player] == "start_with":
|
||||
start_inventory["Pokedex"] = 1
|
||||
self.multiworld.push_precollected(self.create_item("Pokedex"))
|
||||
|
||||
locations = [location for location in location_data if location.type == "Item"]
|
||||
item_pool = []
|
||||
combined_traps = (self.multiworld.poison_trap_weight[self.player].value
|
||||
+ self.multiworld.fire_trap_weight[self.player].value
|
||||
+ self.multiworld.paralyze_trap_weight[self.player].value
|
||||
+ self.multiworld.ice_trap_weight[self.player].value)
|
||||
for location in locations:
|
||||
if not location.inclusion(self.multiworld, self.player):
|
||||
continue
|
||||
|
@ -189,9 +205,18 @@ class PokemonRedBlueWorld(World):
|
|||
item = self.create_filler()
|
||||
elif location.original_item is None:
|
||||
item = self.create_filler()
|
||||
elif location.original_item == "Pokedex":
|
||||
if self.multiworld.randomize_pokedex[self.player] == "vanilla":
|
||||
self.multiworld.get_location(location.name, self.player).event = True
|
||||
location.event = True
|
||||
item = self.create_item("Pokedex")
|
||||
elif location.original_item.startswith("TM"):
|
||||
if self.multiworld.randomize_tm_moves[self.player]:
|
||||
item = self.create_item(location.original_item.split(" ")[0])
|
||||
else:
|
||||
item = self.create_item(location.original_item)
|
||||
else:
|
||||
item = self.create_item(location.original_item)
|
||||
combined_traps = self.multiworld.poison_trap_weight[self.player].value + self.multiworld.fire_trap_weight[self.player].value + self.multiworld.paralyze_trap_weight[self.player].value + self.multiworld.ice_trap_weight[self.player].value
|
||||
if (item.classification == ItemClassification.filler and self.multiworld.random.randint(1, 100)
|
||||
<= self.multiworld.trap_percentage[self.player].value and combined_traps != 0):
|
||||
item = self.create_item(self.select_trap())
|
||||
|
@ -205,9 +230,62 @@ class PokemonRedBlueWorld(World):
|
|||
self.multiworld.itempool += item_pool
|
||||
|
||||
def pre_fill(self) -> None:
|
||||
|
||||
process_wild_pokemon(self)
|
||||
process_static_pokemon(self)
|
||||
pokemon_locs = [location.name for location in location_data if location.type != "Item"]
|
||||
for location in self.multiworld.get_locations(self.player):
|
||||
if location.name in pokemon_locs:
|
||||
location.show_in_spoiler = False
|
||||
|
||||
def intervene(move):
|
||||
accessible_slots = [loc for loc in self.multiworld.get_reachable_locations(test_state, self.player) if loc.type == "Wild Encounter"]
|
||||
move_bit = pow(2, poke_data.hm_moves.index(move) + 2)
|
||||
viable_mons = [mon for mon in self.local_poke_data if self.local_poke_data[mon]["tms"][6] & move_bit]
|
||||
placed_mons = [slot.item.name for slot in accessible_slots]
|
||||
# this sort method doesn't seem to work if you reference the same list being sorted in the lambda
|
||||
placed_mons_copy = placed_mons.copy()
|
||||
placed_mons.sort(key=lambda i: placed_mons_copy.count(i))
|
||||
placed_mon = placed_mons.pop()
|
||||
if self.multiworld.area_1_to_1_mapping[self.player]:
|
||||
zone = " - ".join(placed_mon.split(" - ")[:-1])
|
||||
replace_slots = [slot for slot in accessible_slots if slot.name.startswith(zone) and slot.item.name ==
|
||||
placed_mon]
|
||||
else:
|
||||
replace_slots = [self.multiworld.random.choice([slot for slot in accessible_slots if slot.item.name ==
|
||||
placed_mon])]
|
||||
replace_mon = self.multiworld.random.choice(viable_mons)
|
||||
for replace_slot in replace_slots:
|
||||
replace_slot.item = self.create_item(replace_mon)
|
||||
last_intervene = None
|
||||
while True:
|
||||
intervene_move = None
|
||||
test_state = self.multiworld.get_all_state(False)
|
||||
if not self.multiworld.badgesanity[self.player]:
|
||||
for badge in ["Boulder Badge", "Cascade Badge", "Thunder Badge", "Rainbow Badge", "Soul Badge",
|
||||
"Marsh Badge", "Volcano Badge", "Earth Badge"]:
|
||||
test_state.collect(self.create_item(badge))
|
||||
if not test_state.pokemon_rb_can_surf(self.player):
|
||||
intervene_move = "Surf"
|
||||
if not test_state.pokemon_rb_can_strength(self.player):
|
||||
intervene_move = "Strength"
|
||||
# cut may not be needed if accessibility is minimal, unless you need all 8 badges and badgesanity is off,
|
||||
# as you will require cut to access celadon gyn
|
||||
if (self.multiworld.accessibility[self.player] != "minimal" or ((not
|
||||
self.multiworld.badgesanity[self.player]) and max(self.multiworld.elite_four_condition[self.player],
|
||||
self.multiworld.victory_road_condition[self.player]) > 7)):
|
||||
if not test_state.pokemon_rb_can_cut(self.player):
|
||||
intervene_move = "Cut"
|
||||
if (self.multiworld.accessibility[self.player].current_key != "minimal" and
|
||||
(self.multiworld.trainersanity[self.player] or self.multiworld.extra_key_items[self.player])):
|
||||
if not test_state.pokemon_rb_can_flash(self.player):
|
||||
intervene_move = "Flash"
|
||||
if intervene_move:
|
||||
if intervene_move == last_intervene:
|
||||
raise Exception(f"Caught in infinite loop attempting to ensure {intervene_move} is available to player {self.player}")
|
||||
intervene(intervene_move)
|
||||
last_intervene = intervene_move
|
||||
else:
|
||||
break
|
||||
|
||||
if self.multiworld.old_man[self.player].value == 1:
|
||||
self.multiworld.local_early_items[self.player]["Oak's Parcel"] = 1
|
||||
|
@ -237,17 +315,26 @@ class PokemonRedBlueWorld(World):
|
|||
else:
|
||||
raise FillError(f"Failed to place badges for player {self.player}")
|
||||
|
||||
locs = [self.multiworld.get_location("Fossil - Choice A", self.player),
|
||||
self.multiworld.get_location("Fossil - Choice B", self.player)]
|
||||
for loc in locs:
|
||||
add_item_rule(loc, lambda i: i.advancement or i.name in self.item_name_groups["Unique"]
|
||||
or i.name == "Master Ball")
|
||||
# Place local items in some locations to prevent save-scumming. Also Oak's PC to prevent an "AP Item" from
|
||||
# entering the player's inventory.
|
||||
|
||||
locs = {self.multiworld.get_location("Fossil - Choice A", self.player),
|
||||
self.multiworld.get_location("Fossil - Choice B", self.player)}
|
||||
|
||||
if self.multiworld.dexsanity[self.player]:
|
||||
for mon in ([" ".join(self.multiworld.get_location(
|
||||
f"Pallet Town - Starter {i}", self.player).item.name.split(" ")[1:]) for i in range(1, 4)]
|
||||
+ [" ".join(self.multiworld.get_location(
|
||||
f"Fighting Dojo - Gift {i}", self.player).item.name.split(" ")[1:]) for i in range(1, 3)]):
|
||||
loc = self.multiworld.get_location(f"Pokedex - {mon}", self.player)
|
||||
if loc.item is None:
|
||||
locs.add(loc)
|
||||
|
||||
loc = self.multiworld.get_location("Pallet Town - Player's PC", self.player)
|
||||
if loc.item is None:
|
||||
locs.append(loc)
|
||||
locs.add(loc)
|
||||
|
||||
for loc in locs:
|
||||
for loc in sorted(locs):
|
||||
unplaced_items = []
|
||||
if loc.name in self.multiworld.priority_locations[self.player].value:
|
||||
add_item_rule(loc, lambda i: i.advancement)
|
||||
|
@ -262,21 +349,6 @@ class PokemonRedBlueWorld(World):
|
|||
unplaced_items.append(item)
|
||||
self.multiworld.itempool += unplaced_items
|
||||
|
||||
intervene = False
|
||||
test_state = self.multiworld.get_all_state(False)
|
||||
if not test_state.pokemon_rb_can_surf(self.player) or not test_state.pokemon_rb_can_strength(self.player):
|
||||
intervene = True
|
||||
elif self.multiworld.accessibility[self.player].current_key != "minimal":
|
||||
if not test_state.pokemon_rb_can_cut(self.player) or not test_state.pokemon_rb_can_flash(self.player):
|
||||
intervene = True
|
||||
if intervene:
|
||||
# the way this is handled will be improved significantly in the future when I add options to
|
||||
# let you choose the exact weights for HM compatibility
|
||||
logging.warning(
|
||||
f"HM-compatible Pokémon possibly missing, placing Mew on Route 1 for player {self.player}")
|
||||
loc = self.multiworld.get_location("Route 1 - Wild Pokemon - 1", self.player)
|
||||
loc.item = self.create_item("Mew")
|
||||
|
||||
def create_regions(self):
|
||||
if self.multiworld.free_fly_location[self.player].value:
|
||||
if self.multiworld.old_man[self.player].value == 0:
|
||||
|
@ -317,6 +389,12 @@ class PokemonRedBlueWorld(World):
|
|||
spoiler_handle.write(f"\n\nType matchups ({self.multiworld.player_name[self.player]}):\n\n")
|
||||
for matchup in self.type_chart:
|
||||
spoiler_handle.write(f"{matchup[0]} deals {matchup[2] * 10}% damage to {matchup[1]}\n")
|
||||
spoiler_handle.write(f"\n\nPokémon locations ({self.multiworld.player_name[self.player]}):\n\n")
|
||||
pokemon_locs = [location.name for location in location_data if location.type != "Item"]
|
||||
for location in self.multiworld.get_locations(self.player):
|
||||
if location.name in pokemon_locs:
|
||||
spoiler_handle.write(location.name + ": " + location.item.name + "\n")
|
||||
|
||||
|
||||
def get_filler_item_name(self) -> str:
|
||||
combined_traps = self.multiworld.poison_trap_weight[self.player].value + self.multiworld.fire_trap_weight[self.player].value + self.multiworld.paralyze_trap_weight[self.player].value + self.multiworld.ice_trap_weight[self.player].value
|
||||
|
@ -336,6 +414,21 @@ class PokemonRedBlueWorld(World):
|
|||
self.traps += ["Ice Trap"] * self.multiworld.ice_trap_weight[self.player].value
|
||||
return self.multiworld.random.choice(self.traps)
|
||||
|
||||
def extend_hint_information(self, hint_data):
|
||||
if self.multiworld.dexsanity[self.player]:
|
||||
hint_data[self.player] = {}
|
||||
mon_locations = {mon: set() for mon in poke_data.pokemon_data.keys()}
|
||||
for loc in location_data: #self.multiworld.get_locations(self.player):
|
||||
if loc.type in ["Wild Encounter", "Static Pokemon", "Legendary Pokemon", "Missable Pokemon"]:
|
||||
mon = self.multiworld.get_location(loc.name, self.player).item.name
|
||||
if mon.startswith("Static ") or mon.startswith("Missable "):
|
||||
mon = " ".join(mon.split(" ")[1:])
|
||||
mon_locations[mon].add(loc.name.split(" -")[0])
|
||||
for mon in mon_locations:
|
||||
if mon_locations[mon]:
|
||||
hint_data[self.player][self.multiworld.get_location(f"Pokedex - {mon}", self.player).address] = \
|
||||
", ".join(mon_locations[mon])
|
||||
|
||||
def fill_slot_data(self) -> dict:
|
||||
return {
|
||||
"second_fossil_check_condition": self.multiworld.second_fossil_check_condition[self.player].value,
|
||||
|
@ -358,7 +451,8 @@ class PokemonRedBlueWorld(World):
|
|||
"type_chart": self.type_chart,
|
||||
"randomize_pokedex": self.multiworld.randomize_pokedex[self.player].value,
|
||||
"trainersanity": self.multiworld.trainersanity[self.player].value,
|
||||
"death_link": self.multiworld.death_link[self.player].value
|
||||
"death_link": self.multiworld.death_link[self.player].value,
|
||||
"prizesanity": self.multiworld.prizesanity[self.player].value
|
||||
}
|
||||
|
||||
|
||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -33,6 +33,8 @@ fossil scientist. This may require reviving a number of fossils, depending on yo
|
|||
* If the Old Man is blocking your way through Viridian City, you do not have Oak's Parcel in your inventory, and you've
|
||||
exhausted your money and Poké Balls, you can get a free Poké Ball from your mom.
|
||||
* HM moves can be overwritten if you have the HM for it in your bag.
|
||||
* The NPC on the left behind the Celadon Game Corner counter will sell 1500 coins at once instead of giving information
|
||||
about the Prize Corner
|
||||
|
||||
## What items and locations get shuffled?
|
||||
|
||||
|
|
|
@ -65,7 +65,7 @@ item_table = {
|
|||
"Super Repel": ItemData(56, ItemClassification.filler, ["Consumables"]),
|
||||
"Max Repel": ItemData(57, ItemClassification.filler, ["Consumables"]),
|
||||
"Dire Hit": ItemData(58, ItemClassification.filler, ["Consumables", "Battle Items"]),
|
||||
#"Coin": ItemData(59, ItemClassification.filler),
|
||||
"10 Coins": ItemData(59, ItemClassification.filler, ["Coins"]),
|
||||
"Fresh Water": ItemData(60, ItemClassification.filler, ["Consumables", "Vending Machine Drinks"]),
|
||||
"Soda Pop": ItemData(61, ItemClassification.filler, ["Consumables", "Vending Machine Drinks"]),
|
||||
"Lemonade": ItemData(62, ItemClassification.filler, ["Consumables", "Vending Machine Drinks"]),
|
||||
|
@ -103,6 +103,8 @@ item_table = {
|
|||
"Paralyze Trap": ItemData(95, ItemClassification.trap, ["Traps"]),
|
||||
"Ice Trap": ItemData(96, ItemClassification.trap, ["Traps"]),
|
||||
"Fire Trap": ItemData(97, ItemClassification.trap, ["Traps"]),
|
||||
"20 Coins": ItemData(98, ItemClassification.filler, ["Coins"]),
|
||||
"100 Coins": ItemData(99, ItemClassification.filler, ["Coins"]),
|
||||
"HM01 Cut": ItemData(196, ItemClassification.progression, ["Unique", "HMs"]),
|
||||
"HM02 Fly": ItemData(197, ItemClassification.progression, ["Unique", "HMs"]),
|
||||
"HM03 Surf": ItemData(198, ItemClassification.progression, ["Unique", "HMs"]),
|
||||
|
@ -119,7 +121,7 @@ item_table = {
|
|||
"TM09 Take Down": ItemData(209, ItemClassification.useful, ["Unique", "TMs"]),
|
||||
"TM10 Double Edge": ItemData(210, ItemClassification.useful, ["Unique", "TMs"]),
|
||||
"TM11 Bubble Beam": ItemData(211, ItemClassification.useful, ["Unique", "TMs"]),
|
||||
"TM12 Water Gun": ItemData(212, ItemClassification.useful, ["Unique", "TMs"]),
|
||||
"TM12 Water Gun": ItemData(212, ItemClassification.filler, ["Unique", "TMs"]),
|
||||
"TM13 Ice Beam": ItemData(213, ItemClassification.useful, ["Unique", "TMs"]),
|
||||
"TM14 Blizzard": ItemData(214, ItemClassification.useful, ["Unique", "TMs"]),
|
||||
"TM15 Hyper Beam": ItemData(215, ItemClassification.useful, ["Unique", "TMs"]),
|
||||
|
@ -163,6 +165,10 @@ item_table = {
|
|||
"Silph Co Liberated": ItemData(None, ItemClassification.progression, []),
|
||||
"Become Champion": ItemData(None, ItemClassification.progression, [])
|
||||
}
|
||||
|
||||
item_table.update({f"TM{str(i).zfill(2)}": ItemData(i + 456, ItemClassification.filler, ["Unique", "TMs"])
|
||||
for i in range(1, 51)})
|
||||
|
||||
item_table.update(
|
||||
{pokemon: ItemData(None, ItemClassification.progression, []) for pokemon in pokemon_data.keys()}
|
||||
)
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
|
||||
from BaseClasses import Location
|
||||
from .rom_addresses import rom_addresses
|
||||
from .poke_data import pokemon_data
|
||||
|
||||
loc_id_start = 172000000
|
||||
|
||||
|
||||
|
@ -8,6 +10,10 @@ def trainersanity(multiworld, player):
|
|||
return multiworld.trainersanity[player]
|
||||
|
||||
|
||||
def dexsanity(multiworld, player):
|
||||
return multiworld.dexsanity[player]
|
||||
|
||||
|
||||
def hidden_items(multiworld, player):
|
||||
return multiworld.randomize_hidden_items[player].value > 0
|
||||
|
||||
|
@ -20,14 +26,13 @@ def extra_key_items(multiworld, player):
|
|||
return multiworld.extra_key_items[player]
|
||||
|
||||
|
||||
def pokedex(multiworld, player):
|
||||
return multiworld.randomize_pokedex[player].value > 0
|
||||
|
||||
|
||||
def always_on(multiworld, player):
|
||||
return True
|
||||
|
||||
|
||||
def prizesanity(multiworld, player):
|
||||
return multiworld.prizesanity[player]
|
||||
|
||||
|
||||
class LocationData:
|
||||
|
||||
|
@ -72,6 +77,13 @@ class Rod:
|
|||
self.flag = flag
|
||||
|
||||
|
||||
class DexSanityFlag:
|
||||
def __init__(self, flag):
|
||||
self.byte = int(flag / 8)
|
||||
self.bit = flag % 8
|
||||
self.flag = flag
|
||||
|
||||
|
||||
location_data = [
|
||||
|
||||
LocationData("Vermilion City", "Fishing Guru", "Old Rod", rom_addresses["Rod_Vermilion_City_Fishing_Guru"], Rod(3)),
|
||||
|
@ -119,7 +131,7 @@ location_data = [
|
|||
LocationData("Celadon City", "Gambling Addict", "Coin Case", rom_addresses["Event_Gambling_Addict"],
|
||||
EventFlag(480)),
|
||||
LocationData("Celadon Gym", "Erika 2", "TM21 Mega Drain", rom_addresses["Event_Celadon_Gym"], EventFlag(424)),
|
||||
LocationData("Silph Co 11F", "Silph Co President", "Master Ball", rom_addresses["Event_Silph_Co_President"],
|
||||
LocationData("Silph Co 11F", "Silph Co President (Card Key)", "Master Ball", rom_addresses["Event_Silph_Co_President"],
|
||||
EventFlag(1933)),
|
||||
LocationData("Silph Co 2F", "Woman", "TM36 Self-Destruct", rom_addresses["Event_Scared_Woman"],
|
||||
EventFlag(1791)),
|
||||
|
@ -374,7 +386,7 @@ location_data = [
|
|||
LocationData("Seafoam Islands B4F", "Hidden Item Corner Island", "Ultra Ball", rom_addresses['Hidden_Item_Seafoam_Islands_B4F'], Hidden(26), inclusion=hidden_items),
|
||||
LocationData("Pokemon Mansion 1F", "Hidden Item Block Near Entrance Carpet", "Moon Stone", rom_addresses['Hidden_Item_Pokemon_Mansion_1F'], Hidden(27), inclusion=hidden_items),
|
||||
LocationData("Pokemon Mansion 3F", "Hidden Item Behind Burglar", "Max Revive", rom_addresses['Hidden_Item_Pokemon_Mansion_3F'], Hidden(28), inclusion=hidden_items),
|
||||
LocationData("Route 23", "Hidden Item Rocks Before Final Guard", "Full Restore", rom_addresses['Hidden_Item_Route_23_1'], Hidden(29), inclusion=hidden_items),
|
||||
LocationData("Route 23", "Hidden Item Rocks Before Victory Road", "Full Restore", rom_addresses['Hidden_Item_Route_23_1'], Hidden(29), inclusion=hidden_items),
|
||||
LocationData("Route 23", "Hidden Item East Bush After Water", "Ultra Ball", rom_addresses['Hidden_Item_Route_23_2'], Hidden(30), inclusion=hidden_items),
|
||||
LocationData("Route 23", "Hidden Item On Island", "Max Ether", rom_addresses['Hidden_Item_Route_23_3'], Hidden(31), inclusion=hidden_items),
|
||||
LocationData("Victory Road 2F", "Hidden Item Rock Before Moltres", "Ultra Ball", rom_addresses['Hidden_Item_Victory_Road_2F_1'], Hidden(32), inclusion=hidden_items),
|
||||
|
@ -400,7 +412,8 @@ location_data = [
|
|||
LocationData("Cerulean City", "Hidden Item Gym Badge Guy's Backyard", "Rare Candy", rom_addresses['Hidden_Item_Cerulean_City'], Hidden(52), inclusion=hidden_items),
|
||||
LocationData("Route 4", "Hidden Item Plateau East Of Mt Moon", "Great Ball", rom_addresses['Hidden_Item_Route_4'], Hidden(53), inclusion=hidden_items),
|
||||
|
||||
LocationData("Pallet Town", "Oak's Parcel Reward", "Pokedex", rom_addresses["Event_Pokedex"], EventFlag(0x38), inclusion=pokedex),
|
||||
|
||||
LocationData("Pallet Town", "Oak's Parcel Reward", "Pokedex", rom_addresses["Event_Pokedex"], EventFlag(0x38)),
|
||||
|
||||
LocationData("Pokemon Mansion 1F", "Scientist", None, rom_addresses["Trainersanity_EVENT_BEAT_MANSION_1_TRAINER_0_ITEM"], EventFlag(376), inclusion=trainersanity),
|
||||
LocationData("Pokemon Mansion 2F", "Burglar", None, rom_addresses["Trainersanity_EVENT_BEAT_MANSION_2_TRAINER_0_ITEM"], EventFlag(43), inclusion=trainersanity),
|
||||
|
@ -712,6 +725,36 @@ location_data = [
|
|||
LocationData("Indigo Plateau", "Bruno", None, rom_addresses["Trainersanity_EVENT_BEAT_BRUNOS_ROOM_TRAINER_0_ITEM"], EventFlag(20), inclusion=trainersanity),
|
||||
LocationData("Indigo Plateau", "Agatha", None, rom_addresses["Trainersanity_EVENT_BEAT_AGATHAS_ROOM_TRAINER_0_ITEM"], EventFlag(19), inclusion=trainersanity),
|
||||
LocationData("Indigo Plateau", "Lance", None, rom_addresses["Trainersanity_EVENT_BEAT_LANCES_ROOM_TRAINER_0_ITEM"], EventFlag(18), inclusion=trainersanity),
|
||||
LocationData("Cinnabar Gym", "Burglar 1", None, rom_addresses["Trainersanity_EVENT_BEAT_CINNABAR_GYM_TRAINER_A_ITEM"], EventFlag(374), inclusion=trainersanity),
|
||||
LocationData("Cinnabar Gym", "Super Nerd 1", None, rom_addresses["Trainersanity_EVENT_BEAT_CINNABAR_GYM_TRAINER_B_ITEM"], EventFlag(373), inclusion=trainersanity),
|
||||
LocationData("Cinnabar Gym", "Super Nerd 2", None, rom_addresses["Trainersanity_EVENT_BEAT_CINNABAR_GYM_TRAINER_2_ITEM"], EventFlag(372), inclusion=trainersanity),
|
||||
LocationData("Cinnabar Gym", "Burglar 2", None, rom_addresses["Trainersanity_EVENT_BEAT_CINNABAR_GYM_TRAINER_3_ITEM"], EventFlag(371), inclusion=trainersanity),
|
||||
LocationData("Cinnabar Gym", "Super Nerd 3", None, rom_addresses["Trainersanity_EVENT_BEAT_CINNABAR_GYM_TRAINER_4_ITEM"], EventFlag(370), inclusion=trainersanity),
|
||||
LocationData("Cinnabar Gym", "Super Nerd 4", None, rom_addresses["Trainersanity_EVENT_BEAT_CINNABAR_GYM_TRAINER_5_ITEM"], EventFlag(369), inclusion=trainersanity),
|
||||
LocationData("Cinnabar Gym", "Super Nerd 5", None, rom_addresses["Trainersanity_EVENT_BEAT_CINNABAR_GYM_TRAINER_6_ITEM"], EventFlag(368), inclusion=trainersanity),
|
||||
|
||||
LocationData("Celadon Prize Corner", "Item Prize 1", "TM23 Dragon Rage", rom_addresses["Prize_Item_A"], EventFlag(0x69a), inclusion=prizesanity),
|
||||
LocationData("Celadon Prize Corner", "Item Prize 2", "TM15 Hyper Beam", rom_addresses["Prize_Item_B"], EventFlag(0x69B), inclusion=prizesanity),
|
||||
LocationData("Celadon Prize Corner", "Item Prize 3", "TM50 Substitute", rom_addresses["Prize_Item_C"], EventFlag(0x69C), inclusion=prizesanity),
|
||||
|
||||
LocationData("Celadon Game Corner", "West Gambler's Gift (Coin Case)", "10 Coins", rom_addresses["Event_Game_Corner_Gift_A"], EventFlag(0x1ba)),
|
||||
LocationData("Celadon Game Corner", "Center Gambler's Gift (Coin Case)", "20 Coins", rom_addresses["Event_Game_Corner_Gift_C"], EventFlag(0x1bc)),
|
||||
LocationData("Celadon Game Corner", "East Gambler's Gift (Coin Case)", "20 Coins", rom_addresses["Event_Game_Corner_Gift_B"], EventFlag(0x1bb)),
|
||||
|
||||
LocationData("Celadon Game Corner", "Hidden Item Northwest By Counter (Coin Case)", "10 Coins", rom_addresses["Hidden_Item_Game_Corner_1"], Hidden(54), inclusion=hidden_items),
|
||||
LocationData("Celadon Game Corner", "Hidden Item Southwest Corner (Coin Case)", "10 Coins", rom_addresses["Hidden_Item_Game_Corner_2"], Hidden(55), inclusion=hidden_items),
|
||||
LocationData("Celadon Game Corner", "Hidden Item Near Rumor Man (Coin Case)", "20 Coins", rom_addresses["Hidden_Item_Game_Corner_3"], Hidden(56), inclusion=hidden_items),
|
||||
LocationData("Celadon Game Corner", "Hidden Item Near Speculating Woman (Coin Case)", "10 Coins", rom_addresses["Hidden_Item_Game_Corner_4"], Hidden(57), inclusion=hidden_items),
|
||||
LocationData("Celadon Game Corner", "Hidden Item Near West Gifting Gambler (Coin Case)", "10 Coins", rom_addresses["Hidden_Item_Game_Corner_5"], Hidden(58), inclusion=hidden_items),
|
||||
LocationData("Celadon Game Corner", "Hidden Item Near Wonderful Time Woman (Coin Case)", "20 Coins", rom_addresses["Hidden_Item_Game_Corner_6"], Hidden(59), inclusion=hidden_items),
|
||||
LocationData("Celadon Game Corner", "Hidden Item Near Failing Gym Information Guy (Coin Case)", "10 Coins", rom_addresses["Hidden_Item_Game_Corner_7"], Hidden(60), inclusion=hidden_items),
|
||||
LocationData("Celadon Game Corner", "Hidden Item Near East Gifting Gambler (Coin Case)", "10 Coins", rom_addresses["Hidden_Item_Game_Corner_8"], Hidden(61), inclusion=hidden_items),
|
||||
LocationData("Celadon Game Corner", "Hidden Item Near Hooked Guy (Coin Case)", "10 Coins", rom_addresses["Hidden_Item_Game_Corner_9"], Hidden(62), inclusion=hidden_items),
|
||||
LocationData("Celadon Game Corner", "Hidden Item at End of Horizontal Machine Row (Coin Case)", "20 Coins", rom_addresses["Hidden_Item_Game_Corner_10"], Hidden(63), inclusion=hidden_items),
|
||||
LocationData("Celadon Game Corner", "Hidden Item in Front of Horizontal Machine Row (Coin Case)", "100 Coins", rom_addresses["Hidden_Item_Game_Corner_11"], Hidden(64), inclusion=hidden_items),
|
||||
|
||||
*[LocationData("Pokedex", mon, None, rom_addresses["Dexsanity_Items"] + i, DexSanityFlag(i),
|
||||
type="Item", inclusion=dexsanity) for (mon, i) in zip(pokemon_data.keys(), range(0, 152))],
|
||||
|
||||
LocationData("Indigo Plateau", "Become Champion", "Become Champion", event=True),
|
||||
LocationData("Pokemon Tower 7F", "Fuji Saved", "Fuji Saved", event=True),
|
||||
|
@ -1965,6 +2008,25 @@ location_data = [
|
|||
LocationData("Cinnabar Island", "Dome Fossil Pokemon", "Kabuto", rom_addresses["Gift_Kabuto"], None,
|
||||
event=True, type="Static Pokemon"),
|
||||
|
||||
LocationData("Route 2 East", "Marcel Trade", "Mr Mime", rom_addresses["Trade_Marcel"] + 1, None, event=True,
|
||||
type="Static Pokemon"),
|
||||
LocationData("Underground Tunnel North-South", "Spot Trade", "Nidoran F", rom_addresses["Trade_Spot"] + 1, None,
|
||||
event=True, type="Static Pokemon"),
|
||||
LocationData("Route 11", "Terry Trade", "Nidorina", rom_addresses["Trade_Terry"] + 1, None, event=True,
|
||||
type="Static Pokemon"),
|
||||
LocationData("Route 18", "Marc Trade", "Lickitung", rom_addresses["Trade_Marc"] + 1, None, event=True,
|
||||
type="Static Pokemon"),
|
||||
LocationData("Cinnabar Island", "Sailor Trade", "Seel", rom_addresses["Trade_Sailor"] + 1, None, event=True,
|
||||
type="Static Pokemon"),
|
||||
LocationData("Cinnabar Island", "Crinkles Trade", "Tangela", rom_addresses["Trade_Crinkles"] + 1, None,
|
||||
event=True, type="Static Pokemon"),
|
||||
LocationData("Cinnabar Island", "Doris Trade", "Electrode", rom_addresses["Trade_Doris"] + 1, None,
|
||||
event=True, type="Static Pokemon"),
|
||||
LocationData("Vermilion City", "Dux Trade", "Farfetchd", rom_addresses["Trade_Dux"] + 1, None, event=True,
|
||||
type="Static Pokemon"),
|
||||
LocationData("Cerulean City", "Lola Trade", "Jynx", rom_addresses["Trade_Lola"] + 1, None, event=True,
|
||||
type="Static Pokemon"),
|
||||
|
||||
# not counted for logic currently. Could perhaps make static encounters resettable in the future?
|
||||
LocationData("Power Plant", "Fake Pokeball Battle 1", "Voltorb", rom_addresses["Static_Encounter_Voltorb_A"],
|
||||
None, event=True, type="Missable Pokemon"),
|
||||
|
@ -2043,20 +2105,24 @@ location_data = [
|
|||
|
||||
]
|
||||
|
||||
for i, location in enumerate(location_data):
|
||||
|
||||
|
||||
i = 0
|
||||
for location in location_data:
|
||||
if location.event or location.rom_address is None:
|
||||
location.address = None
|
||||
else:
|
||||
location.address = loc_id_start + i
|
||||
|
||||
i += 1
|
||||
|
||||
|
||||
class PokemonRBLocation(Location):
|
||||
game = "Pokemon Red and Blue"
|
||||
|
||||
def __init__(self, player, name, address, rom_address):
|
||||
def __init__(self, player, name, address, rom_address, type):
|
||||
super(PokemonRBLocation, self).__init__(
|
||||
player, name,
|
||||
address
|
||||
)
|
||||
self.rom_address = rom_address
|
||||
self.rom_address = rom_address
|
||||
self.type = type
|
||||
|
|
|
@ -45,14 +45,13 @@ class PokemonLogic(LogicMixin):
|
|||
["Boulder Badge", "Cascade Badge", "Thunder Badge", "Rainbow Badge", "Soul Badge", "Marsh Badge",
|
||||
"Volcano Badge", "Earth Badge", "Bicycle", "Silph Scope", "Item Finder", "Super Rod", "Good Rod",
|
||||
"Old Rod", "Lift Key", "Card Key", "Town Map", "Coin Case", "S.S. Ticket", "Secret Key",
|
||||
"Mansion Key", "Safari Pass", "Plant Key", "Hideout Key", "HM01 Cut", "HM02 Fly", "HM03 Surf",
|
||||
"HM04 Strength", "HM05 Flash"] if self.has(item, player)]) >= count
|
||||
"Poke Flute", "Mansion Key", "Safari Pass", "Plant Key", "Hideout Key", "HM01 Cut", "HM02 Fly",
|
||||
"HM03 Surf", "HM04 Strength", "HM05 Flash"] if self.has(item, player)]) >= count
|
||||
|
||||
def pokemon_rb_can_pass_guards(self, player):
|
||||
if self.multiworld.tea[player].value:
|
||||
return self.has("Tea", player)
|
||||
else:
|
||||
# this could just be "True", but you never know what weird options I might introduce later ;)
|
||||
return self.can_reach("Celadon City - Counter Man", "Location", player)
|
||||
|
||||
def pokemon_rb_has_badges(self, count, player):
|
||||
|
@ -60,13 +59,8 @@ class PokemonLogic(LogicMixin):
|
|||
"Soul Badge", "Volcano Badge", "Earth Badge"] if self.has(item, player)]) >= count
|
||||
|
||||
def pokemon_rb_oaks_aide(self, count, player):
|
||||
if self.multiworld.randomize_pokedex[player].value > 0:
|
||||
if not self.has("Pokedex", player):
|
||||
return False
|
||||
else:
|
||||
if not self.has("Oak's Parcel", player):
|
||||
return False
|
||||
return self.pokemon_rb_has_pokemon(count, player)
|
||||
return ((not self.multiworld.require_pokedex[player] or self.has("Pokedex", player))
|
||||
and self.pokemon_rb_has_pokemon(count, player))
|
||||
|
||||
def pokemon_rb_has_pokemon(self, count, player):
|
||||
obtained_pokemon = set()
|
||||
|
|
|
@ -65,7 +65,7 @@ class CeruleanCaveCondition(Range):
|
|||
If extra_key_items is turned on, the number chosen will be increased by 4."""
|
||||
display_name = "Cerulean Cave Condition"
|
||||
range_start = 0
|
||||
range_end = 25
|
||||
range_end = 26
|
||||
default = 20
|
||||
|
||||
|
||||
|
@ -155,6 +155,12 @@ class RandomizeHiddenItems(Choice):
|
|||
default = 0
|
||||
|
||||
|
||||
class PrizeSanity(Toggle):
|
||||
"""Shuffles the TM prizes at the Celadon Prize Corner into the item pool."""
|
||||
display_name = "Prizesanity"
|
||||
default = 0
|
||||
|
||||
|
||||
class TrainerSanity(Toggle):
|
||||
"""Add a location check to every trainer in the game, which can be obtained by talking to a trainer after defeating
|
||||
them. Does not affect gym leaders and some scripted event battles (including all Rival, Giovanni, and
|
||||
|
@ -163,12 +169,44 @@ class TrainerSanity(Toggle):
|
|||
default = 0
|
||||
|
||||
|
||||
class RequirePokedex(Toggle):
|
||||
"""Require the Pokedex to obtain items from Oak's Aides or from Dexsanity checks."""
|
||||
display_name = "Require Pokedex"
|
||||
default = 1
|
||||
|
||||
|
||||
class AllPokemonSeen(Toggle):
|
||||
"""Start with all Pokemon "seen" in your Pokedex. This allows you to see where Pokemon can be encountered in the
|
||||
wild. Pokemon found by fishing or in the Cerulean Cave are not displayed."""
|
||||
default = 0
|
||||
|
||||
|
||||
class DexSanity(Toggle):
|
||||
"""Adds a location check for each Pokemon flagged "Owned" on your Pokedex. If accessibility is set to `locations`
|
||||
and randomize_wild_pokemon is off, catch_em_all is not `all_pokemon` or randomize_legendary_pokemon is not `any`,
|
||||
accessibility will be forced to `items` instead, as not all Dexsanity locations can be guaranteed to be considered
|
||||
reachable in logic.
|
||||
If Pokedex is required, the items for Pokemon acquired before acquiring the Pokedex can be found by talking to
|
||||
Professor Oak or evaluating the Pokedex via Oak's PC."""
|
||||
display_name = "Dexsanity"
|
||||
default = 0
|
||||
|
||||
|
||||
class FreeFlyLocation(Toggle):
|
||||
"""One random fly destination will be unlocked by default."""
|
||||
display_name = "Free Fly Location"
|
||||
default = 1
|
||||
|
||||
|
||||
class RandomizeRockTunnel(Toggle):
|
||||
"""Randomize the layout of Rock Tunnel. This is highly experimental, if you encounter any issues (items or trainers
|
||||
unreachable, trainers walking over walls, inability to reach end of tunnel, anything looking strange) to
|
||||
Alchav#8826 in the Archipelago Discord (directly or in #pkmn-red-blue) along with the seed number found on the
|
||||
signs outside the tunnel."""
|
||||
display_name = "Randomize Rock Tunnel"
|
||||
default = 0
|
||||
|
||||
|
||||
class OaksAidRt2(Range):
|
||||
"""Number of Pokemon registered in the Pokedex required to receive the item from Oak's Aide on Route 2.
|
||||
Vanilla is 10."""
|
||||
|
@ -229,6 +267,12 @@ class RandomizeWildPokemon(Choice):
|
|||
option_completely_random = 4
|
||||
|
||||
|
||||
class Area1To1Mapping(Toggle):
|
||||
"""When randomizing wild Pokemon, for each zone, all instances of a particular Pokemon will be replaced with the
|
||||
same Pokemon, resulting in fewer Pokemon in each area."""
|
||||
default = 1
|
||||
|
||||
|
||||
class RandomizeStarterPokemon(Choice):
|
||||
"""Randomize the starter Pokemon choices."""
|
||||
display_name = "Randomize Starter Pokemon"
|
||||
|
@ -334,6 +378,13 @@ class MinimumCatchRate(Range):
|
|||
default = 3
|
||||
|
||||
|
||||
class MoveBalancing(Toggle):
|
||||
"""All one-hit-KO moves and fixed-damage moves become normal damaging moves.
|
||||
Blizzard, and moves that cause sleep have their accuracy reduced."""
|
||||
display_name = "Move Balancing"
|
||||
default = 0
|
||||
|
||||
|
||||
class RandomizePokemonMovesets(Choice):
|
||||
"""Randomize the moves learned by Pokemon. prefer_types will prefer moves that match the type of the Pokemon."""
|
||||
display_name = "Randomize Pokemon Movesets"
|
||||
|
@ -343,6 +394,12 @@ class RandomizePokemonMovesets(Choice):
|
|||
default = 0
|
||||
|
||||
|
||||
class ConfineTranstormToDitto(Toggle):
|
||||
"""Regardless of moveset randomization, will keep Ditto's first move as Transform no others will learn it.
|
||||
If an enemy Pokemon uses transform before you catch it, it will permanently change to Ditto after capture."""
|
||||
display_name = "Confine Transform to Ditto"
|
||||
default = 1
|
||||
|
||||
class StartWithFourMoves(Toggle):
|
||||
"""If movesets are randomized, this will give all Pokemon 4 starting moves."""
|
||||
display_name = "Start With Four Moves"
|
||||
|
@ -356,30 +413,62 @@ class SameTypeAttackBonus(Toggle):
|
|||
default = 1
|
||||
|
||||
|
||||
class TMCompatibility(Choice):
|
||||
"""Randomize which Pokemon can learn each TM. prefer_types: 90% chance if Pokemon's type matches the move,
|
||||
50% chance if move is Normal type and the Pokemon is not, and 25% chance otherwise. Pokemon will retain the same
|
||||
TM compatibility when they evolve if the evolved form has the same type(s). Mew will always be able to learn
|
||||
every TM."""
|
||||
display_name = "TM Compatibility"
|
||||
default = 0
|
||||
option_vanilla = 0
|
||||
option_prefer_types = 1
|
||||
option_completely_random = 2
|
||||
option_full_compatibility = 3
|
||||
class RandomizeTMMoves(Toggle):
|
||||
"""Randomize the moves taught by TMs.
|
||||
All TM items will be flagged as 'filler' items regardless of how good the move they teach are."""
|
||||
display_name = "Randomize TM Moves"
|
||||
|
||||
|
||||
class HMCompatibility(Choice):
|
||||
"""Randomize which Pokemon can learn each HM. prefer_types: 100% chance if Pokemon's type matches the move,
|
||||
75% chance if move is Normal type and the Pokemon is not, and 25% chance otherwise. Pokemon will retain the same
|
||||
HM compatibility when they evolve if the evolved form has the same type(s). Mew will always be able to learn
|
||||
every HM."""
|
||||
display_name = "HM Compatibility"
|
||||
default = 0
|
||||
option_vanilla = 0
|
||||
option_prefer_types = 1
|
||||
option_completely_random = 2
|
||||
option_full_compatibility = 3
|
||||
class TMHMCompatibility(SpecialRange):
|
||||
range_start = -1
|
||||
range_end = 100
|
||||
special_range_names = {
|
||||
"vanilla": -1,
|
||||
"none": 0,
|
||||
"full": 100
|
||||
}
|
||||
default = -1
|
||||
|
||||
|
||||
class TMSameTypeCompatibility(TMHMCompatibility):
|
||||
"""Chance of each TM being usable on each Pokemon whose type matches the move."""
|
||||
display_name = "TM Same-Type Compatibility"
|
||||
|
||||
|
||||
class TMNormalTypeCompatibility(TMHMCompatibility):
|
||||
"""Chance of each TM being usable on each Pokemon if the move is Normal type and the Pokemon is not."""
|
||||
display_name = "TM Normal-Type Compatibility"
|
||||
|
||||
|
||||
class TMOtherTypeCompatibility(TMHMCompatibility):
|
||||
"""Chance of each TM being usable on each Pokemon if the move a type other than Normal or one of the Pokemon's types."""
|
||||
display_name = "TM Other-Type Compatibility"
|
||||
|
||||
|
||||
class HMSameTypeCompatibility(TMHMCompatibility):
|
||||
"""Chance of each HM being usable on each Pokemon whose type matches the move.
|
||||
At least one Pokemon will always be able to learn the moves needed to meet your accessibility requirements."""
|
||||
display_name = "HM Same-Type Compatibility"
|
||||
|
||||
|
||||
class HMNormalTypeCompatibility(TMHMCompatibility):
|
||||
"""Chance of each HM being usable on each Pokemon if the move is Normal type and the Pokemon is not.
|
||||
At least one Pokemon will always be able to learn the moves needed to meet your accessibility requirements."""
|
||||
display_name = "HM Normal-Type Compatibility"
|
||||
|
||||
|
||||
class HMOtherTypeCompatibility(TMHMCompatibility):
|
||||
"""Chance of each HM being usable on each Pokemon if the move a type other than Normal or one of the Pokemon's types.
|
||||
At least one Pokemon will always be able to learn the moves needed to meet your accessibility requirements."""
|
||||
display_name = "HM Other-Type Compatibility"
|
||||
|
||||
|
||||
class InheritTMHMCompatibility(Toggle):
|
||||
"""If on, evolved Pokemon will inherit their pre-evolved form's TM and HM compatibilities.
|
||||
They will then roll the above set chances again at a 50% lower rate for all TMs and HMs their predecessor could not
|
||||
learn, unless the evolved form has additional or different types, then moves of those new types will be rolled
|
||||
at the full set chance."""
|
||||
display_name = "Inherit TM/HM Compatibility"
|
||||
|
||||
|
||||
class RandomizePokemonTypes(Choice):
|
||||
|
@ -543,6 +632,17 @@ class IceTrapWeight(TrapWeight):
|
|||
default = 0
|
||||
|
||||
|
||||
class RandomizePokemonPalettes(Choice):
|
||||
"""Modify palettes of Pokemon. Primary Type will set Pokemons' palettes based on their primary type, Follow
|
||||
Evolutions will randomize palettes but palettes will remain the same through evolutions (except Eeveelutions),
|
||||
Completely Random will randomize all Pokemons' palettes individually"""
|
||||
display_name = "Randomize Pokemon Palettes"
|
||||
option_vanilla = 0
|
||||
option_primary_type = 1
|
||||
option_follow_evolutions = 2
|
||||
option_completely_random = 3
|
||||
|
||||
|
||||
pokemon_rb_options = {
|
||||
"game_version": GameVersion,
|
||||
"trainer_name": TrainerName,
|
||||
|
@ -561,16 +661,22 @@ pokemon_rb_options = {
|
|||
"extra_strength_boulders": ExtraStrengthBoulders,
|
||||
"require_item_finder": RequireItemFinder,
|
||||
"randomize_hidden_items": RandomizeHiddenItems,
|
||||
"prizesanity": PrizeSanity,
|
||||
"trainersanity": TrainerSanity,
|
||||
"badges_needed_for_hm_moves": BadgesNeededForHMMoves,
|
||||
"free_fly_location": FreeFlyLocation,
|
||||
"require_pokedex": RequirePokedex,
|
||||
"all_pokemon_seen": AllPokemonSeen,
|
||||
"dexsanity": DexSanity,
|
||||
"oaks_aide_rt_2": OaksAidRt2,
|
||||
"oaks_aide_rt_11": OaksAidRt11,
|
||||
"oaks_aide_rt_15": OaksAidRt15,
|
||||
"badges_needed_for_hm_moves": BadgesNeededForHMMoves,
|
||||
"free_fly_location": FreeFlyLocation,
|
||||
"randomize_rock_tunnel": RandomizeRockTunnel,
|
||||
"blind_trainers": BlindTrainers,
|
||||
"minimum_steps_between_encounters": MinimumStepsBetweenEncounters,
|
||||
"exp_modifier": ExpModifier,
|
||||
"randomize_wild_pokemon": RandomizeWildPokemon,
|
||||
"area_1_to_1_mapping": Area1To1Mapping,
|
||||
"randomize_starter_pokemon": RandomizeStarterPokemon,
|
||||
"randomize_static_pokemon": RandomizeStaticPokemon,
|
||||
"randomize_legendary_pokemon": RandomizeLegendaryPokemon,
|
||||
|
@ -580,11 +686,19 @@ pokemon_rb_options = {
|
|||
"minimum_catch_rate": MinimumCatchRate,
|
||||
"randomize_trainer_parties": RandomizeTrainerParties,
|
||||
"trainer_legendaries": TrainerLegendaries,
|
||||
"move_balancing": MoveBalancing,
|
||||
"randomize_pokemon_movesets": RandomizePokemonMovesets,
|
||||
"confine_transform_to_ditto": ConfineTranstormToDitto,
|
||||
"start_with_four_moves": StartWithFourMoves,
|
||||
"same_type_attack_bonus": SameTypeAttackBonus,
|
||||
"tm_compatibility": TMCompatibility,
|
||||
"hm_compatibility": HMCompatibility,
|
||||
"randomize_tm_moves": RandomizeTMMoves,
|
||||
"tm_same_type_compatibility": TMSameTypeCompatibility,
|
||||
"tm_normal_type_compatibility": TMNormalTypeCompatibility,
|
||||
"tm_other_type_compatibility": TMOtherTypeCompatibility,
|
||||
"hm_same_type_compatibility": HMSameTypeCompatibility,
|
||||
"hm_normal_type_compatibility": HMNormalTypeCompatibility,
|
||||
"hm_other_type_compatibility": HMOtherTypeCompatibility,
|
||||
"inherit_tm_hm_compatibility": InheritTMHMCompatibility,
|
||||
"randomize_pokemon_types": RandomizePokemonTypes,
|
||||
"secondary_type_chance": SecondaryTypeChance,
|
||||
"randomize_type_chart": RandomizeTypeChart,
|
||||
|
@ -604,5 +718,6 @@ pokemon_rb_options = {
|
|||
"fire_trap_weight": FireTrapWeight,
|
||||
"paralyze_trap_weight": ParalyzeTrapWeight,
|
||||
"ice_trap_weight": IceTrapWeight,
|
||||
"randomize_pokemon_palettes": RandomizePokemonPalettes,
|
||||
"death_link": DeathLink
|
||||
}
|
||||
|
|
|
@ -1006,172 +1006,172 @@ learnsets = {
|
|||
}
|
||||
|
||||
moves = {
|
||||
'No Move': {'id': 0, 'power': 0, 'type': 'Typeless', 'accuracy': 0, 'pp': 0},
|
||||
'Pound': {'id': 1, 'power': 40, 'type': 'Normal', 'accuracy': 100, 'pp': 35},
|
||||
'Karate Chop': {'id': 2, 'power': 50, 'type': 'Normal', 'accuracy': 100, 'pp': 25},
|
||||
'Doubleslap': {'id': 3, 'power': 15, 'type': 'Normal', 'accuracy': 85, 'pp': 10},
|
||||
'Comet Punch': {'id': 4, 'power': 18, 'type': 'Normal', 'accuracy': 85, 'pp': 15},
|
||||
'Mega Punch': {'id': 5, 'power': 80, 'type': 'Normal', 'accuracy': 85, 'pp': 20},
|
||||
'Pay Day': {'id': 6, 'power': 40, 'type': 'Normal', 'accuracy': 100, 'pp': 20},
|
||||
'Fire Punch': {'id': 7, 'power': 75, 'type': 'Fire', 'accuracy': 100, 'pp': 15},
|
||||
'Ice Punch': {'id': 8, 'power': 75, 'type': 'Ice', 'accuracy': 100, 'pp': 15},
|
||||
'Thunderpunch': {'id': 9, 'power': 75, 'type': 'Electric', 'accuracy': 100, 'pp': 15},
|
||||
'Scratch': {'id': 10, 'power': 40, 'type': 'Normal', 'accuracy': 100, 'pp': 35},
|
||||
'Vicegrip': {'id': 11, 'power': 55, 'type': 'Normal', 'accuracy': 100, 'pp': 30},
|
||||
'Guillotine': {'id': 12, 'power': 1, 'type': 'Normal', 'accuracy': 30, 'pp': 5},
|
||||
'Razor Wind': {'id': 13, 'power': 80, 'type': 'Normal', 'accuracy': 75, 'pp': 10},
|
||||
'Swords Dance': {'id': 14, 'power': 0, 'type': 'Normal', 'accuracy': 100, 'pp': 30},
|
||||
'Cut': {'id': 15, 'power': 50, 'type': 'Normal', 'accuracy': 95, 'pp': 30},
|
||||
'Gust': {'id': 16, 'power': 40, 'type': 'Normal', 'accuracy': 100, 'pp': 35},
|
||||
'Wing Attack': {'id': 17, 'power': 35, 'type': 'Flying', 'accuracy': 100, 'pp': 35},
|
||||
'Whirlwind': {'id': 18, 'power': 0, 'type': 'Normal', 'accuracy': 85, 'pp': 20},
|
||||
'Fly': {'id': 19, 'power': 70, 'type': 'Flying', 'accuracy': 95, 'pp': 15},
|
||||
'Bind': {'id': 20, 'power': 15, 'type': 'Normal', 'accuracy': 75, 'pp': 20},
|
||||
'Slam': {'id': 21, 'power': 80, 'type': 'Normal', 'accuracy': 75, 'pp': 20},
|
||||
'Vine Whip': {'id': 22, 'power': 35, 'type': 'Grass', 'accuracy': 100, 'pp': 10},
|
||||
'Stomp': {'id': 23, 'power': 65, 'type': 'Normal', 'accuracy': 100, 'pp': 20},
|
||||
'Double Kick': {'id': 24, 'power': 30, 'type': 'Fighting', 'accuracy': 100, 'pp': 30},
|
||||
'Mega Kick': {'id': 25, 'power': 120, 'type': 'Normal', 'accuracy': 75, 'pp': 5},
|
||||
'Jump Kick': {'id': 26, 'power': 70, 'type': 'Fighting', 'accuracy': 95, 'pp': 25},
|
||||
'Rolling Kick': {'id': 27, 'power': 60, 'type': 'Fighting', 'accuracy': 85, 'pp': 15},
|
||||
'Sand Attack': {'id': 28, 'power': 0, 'type': 'Normal', 'accuracy': 100, 'pp': 15},
|
||||
'Headbutt': {'id': 29, 'power': 70, 'type': 'Normal', 'accuracy': 100, 'pp': 15},
|
||||
'Horn Attack': {'id': 30, 'power': 65, 'type': 'Normal', 'accuracy': 100, 'pp': 25},
|
||||
'Fury Attack': {'id': 31, 'power': 15, 'type': 'Normal', 'accuracy': 85, 'pp': 20},
|
||||
'Horn Drill': {'id': 32, 'power': 1, 'type': 'Normal', 'accuracy': 30, 'pp': 5},
|
||||
'Tackle': {'id': 33, 'power': 35, 'type': 'Normal', 'accuracy': 95, 'pp': 35},
|
||||
'Body Slam': {'id': 34, 'power': 85, 'type': 'Normal', 'accuracy': 100, 'pp': 15},
|
||||
'Wrap': {'id': 35, 'power': 15, 'type': 'Normal', 'accuracy': 85, 'pp': 20},
|
||||
'Take Down': {'id': 36, 'power': 90, 'type': 'Normal', 'accuracy': 85, 'pp': 20},
|
||||
'Thrash': {'id': 37, 'power': 90, 'type': 'Normal', 'accuracy': 100, 'pp': 20},
|
||||
'Double Edge': {'id': 38, 'power': 100, 'type': 'Normal', 'accuracy': 100, 'pp': 15},
|
||||
'Tail Whip': {'id': 39, 'power': 0, 'type': 'Normal', 'accuracy': 100, 'pp': 30},
|
||||
'Poison Sting': {'id': 40, 'power': 15, 'type': 'Poison', 'accuracy': 100, 'pp': 35},
|
||||
'Twineedle': {'id': 41, 'power': 25, 'type': 'Bug', 'accuracy': 100, 'pp': 20},
|
||||
'Pin Missile': {'id': 42, 'power': 14, 'type': 'Bug', 'accuracy': 85, 'pp': 20},
|
||||
'Leer': {'id': 43, 'power': 0, 'type': 'Normal', 'accuracy': 100, 'pp': 30},
|
||||
'Bite': {'id': 44, 'power': 60, 'type': 'Normal', 'accuracy': 100, 'pp': 25},
|
||||
'Growl': {'id': 45, 'power': 0, 'type': 'Normal', 'accuracy': 100, 'pp': 40},
|
||||
'Roar': {'id': 46, 'power': 0, 'type': 'Normal', 'accuracy': 100, 'pp': 20},
|
||||
'Sing': {'id': 47, 'power': 0, 'type': 'Normal', 'accuracy': 55, 'pp': 15},
|
||||
'Supersonic': {'id': 48, 'power': 0, 'type': 'Normal', 'accuracy': 55, 'pp': 20},
|
||||
'Sonicboom': {'id': 49, 'power': 1, 'type': 'Normal', 'accuracy': 90, 'pp': 20},
|
||||
'Disable': {'id': 50, 'power': 0, 'type': 'Normal', 'accuracy': 55, 'pp': 20},
|
||||
'Acid': {'id': 51, 'power': 40, 'type': 'Poison', 'accuracy': 100, 'pp': 30},
|
||||
'Ember': {'id': 52, 'power': 40, 'type': 'Fire', 'accuracy': 100, 'pp': 25},
|
||||
'Flamethrower': {'id': 53, 'power': 95, 'type': 'Fire', 'accuracy': 100, 'pp': 15},
|
||||
'Mist': {'id': 54, 'power': 0, 'type': 'Ice', 'accuracy': 100, 'pp': 30},
|
||||
'Water Gun': {'id': 55, 'power': 40, 'type': 'Water', 'accuracy': 100, 'pp': 25},
|
||||
'Hydro Pump': {'id': 56, 'power': 120, 'type': 'Water', 'accuracy': 80, 'pp': 5},
|
||||
'Surf': {'id': 57, 'power': 95, 'type': 'Water', 'accuracy': 100, 'pp': 15},
|
||||
'Ice Beam': {'id': 58, 'power': 95, 'type': 'Ice', 'accuracy': 100, 'pp': 10},
|
||||
'Blizzard': {'id': 59, 'power': 120, 'type': 'Ice', 'accuracy': 90, 'pp': 5},
|
||||
'Psybeam': {'id': 60, 'power': 65, 'type': 'Psychic', 'accuracy': 100, 'pp': 20},
|
||||
'Bubblebeam': {'id': 61, 'power': 65, 'type': 'Water', 'accuracy': 100, 'pp': 20},
|
||||
'Aurora Beam': {'id': 62, 'power': 65, 'type': 'Ice', 'accuracy': 100, 'pp': 20},
|
||||
'Hyper Beam': {'id': 63, 'power': 150, 'type': 'Normal', 'accuracy': 90, 'pp': 5},
|
||||
'Peck': {'id': 64, 'power': 35, 'type': 'Flying', 'accuracy': 100, 'pp': 35},
|
||||
'Drill Peck': {'id': 65, 'power': 80, 'type': 'Flying', 'accuracy': 100, 'pp': 20},
|
||||
'Submission': {'id': 66, 'power': 80, 'type': 'Fighting', 'accuracy': 80, 'pp': 25},
|
||||
'Low Kick': {'id': 67, 'power': 50, 'type': 'Fighting', 'accuracy': 90, 'pp': 20},
|
||||
'Counter': {'id': 68, 'power': 1, 'type': 'Fighting', 'accuracy': 100, 'pp': 20},
|
||||
'Seismic Toss': {'id': 69, 'power': 1, 'type': 'Fighting', 'accuracy': 100, 'pp': 20},
|
||||
'Strength': {'id': 70, 'power': 80, 'type': 'Normal', 'accuracy': 100, 'pp': 15},
|
||||
'Absorb': {'id': 71, 'power': 20, 'type': 'Grass', 'accuracy': 100, 'pp': 20},
|
||||
'Mega Drain': {'id': 72, 'power': 40, 'type': 'Grass', 'accuracy': 100, 'pp': 10},
|
||||
'Leech Seed': {'id': 73, 'power': 0, 'type': 'Grass', 'accuracy': 90, 'pp': 10},
|
||||
'Growth': {'id': 74, 'power': 0, 'type': 'Normal', 'accuracy': 100, 'pp': 40},
|
||||
'Razor Leaf': {'id': 75, 'power': 55, 'type': 'Grass', 'accuracy': 95, 'pp': 25},
|
||||
'Solarbeam': {'id': 76, 'power': 120, 'type': 'Grass', 'accuracy': 100, 'pp': 10},
|
||||
'Poisonpowder': {'id': 77, 'power': 0, 'type': 'Poison', 'accuracy': 75, 'pp': 35},
|
||||
'Stun Spore': {'id': 78, 'power': 0, 'type': 'Grass', 'accuracy': 75, 'pp': 30},
|
||||
'Sleep Powder': {'id': 79, 'power': 0, 'type': 'Grass', 'accuracy': 75, 'pp': 15},
|
||||
'Petal Dance': {'id': 80, 'power': 70, 'type': 'Grass', 'accuracy': 100, 'pp': 20},
|
||||
'String Shot': {'id': 81, 'power': 0, 'type': 'Bug', 'accuracy': 95, 'pp': 40},
|
||||
'Dragon Rage': {'id': 82, 'power': 1, 'type': 'Dragon', 'accuracy': 100, 'pp': 10},
|
||||
'Fire Spin': {'id': 83, 'power': 15, 'type': 'Fire', 'accuracy': 70, 'pp': 15},
|
||||
'Thundershock': {'id': 84, 'power': 40, 'type': 'Electric', 'accuracy': 100, 'pp': 30},
|
||||
'Thunderbolt': {'id': 85, 'power': 95, 'type': 'Electric', 'accuracy': 100, 'pp': 15},
|
||||
'Thunder Wave': {'id': 86, 'power': 0, 'type': 'Electric', 'accuracy': 100, 'pp': 20},
|
||||
'Thunder': {'id': 87, 'power': 120, 'type': 'Electric', 'accuracy': 70, 'pp': 10},
|
||||
'Rock Throw': {'id': 88, 'power': 50, 'type': 'Rock', 'accuracy': 65, 'pp': 15},
|
||||
'Earthquake': {'id': 89, 'power': 100, 'type': 'Ground', 'accuracy': 100, 'pp': 10},
|
||||
'Fissure': {'id': 90, 'power': 1, 'type': 'Ground', 'accuracy': 30, 'pp': 5},
|
||||
'Dig': {'id': 91, 'power': 100, 'type': 'Ground', 'accuracy': 100, 'pp': 10},
|
||||
'Toxic': {'id': 92, 'power': 0, 'type': 'Poison', 'accuracy': 85, 'pp': 10},
|
||||
'Confusion': {'id': 93, 'power': 50, 'type': 'Psychic', 'accuracy': 100, 'pp': 25},
|
||||
'Psychic': {'id': 94, 'power': 90, 'type': 'Psychic', 'accuracy': 100, 'pp': 10},
|
||||
'Hypnosis': {'id': 95, 'power': 0, 'type': 'Psychic', 'accuracy': 60, 'pp': 20},
|
||||
'Meditate': {'id': 96, 'power': 0, 'type': 'Psychic', 'accuracy': 100, 'pp': 40},
|
||||
'Agility': {'id': 97, 'power': 0, 'type': 'Psychic', 'accuracy': 100, 'pp': 30},
|
||||
'Quick Attack': {'id': 98, 'power': 40, 'type': 'Normal', 'accuracy': 100, 'pp': 30},
|
||||
'Rage': {'id': 99, 'power': 20, 'type': 'Normal', 'accuracy': 100, 'pp': 20},
|
||||
'Teleport': {'id': 100, 'power': 0, 'type': 'Psychic', 'accuracy': 100, 'pp': 20},
|
||||
'Night Shade': {'id': 101, 'power': 0, 'type': 'Ghost', 'accuracy': 100, 'pp': 15},
|
||||
'Mimic': {'id': 102, 'power': 0, 'type': 'Normal', 'accuracy': 100, 'pp': 10},
|
||||
'Screech': {'id': 103, 'power': 0, 'type': 'Normal', 'accuracy': 85, 'pp': 40},
|
||||
'Double Team': {'id': 104, 'power': 0, 'type': 'Normal', 'accuracy': 100, 'pp': 15},
|
||||
'Recover': {'id': 105, 'power': 0, 'type': 'Normal', 'accuracy': 100, 'pp': 20},
|
||||
'Harden': {'id': 106, 'power': 0, 'type': 'Normal', 'accuracy': 100, 'pp': 30},
|
||||
'Minimize': {'id': 107, 'power': 0, 'type': 'Normal', 'accuracy': 100, 'pp': 20},
|
||||
'Smokescreen': {'id': 108, 'power': 0, 'type': 'Normal', 'accuracy': 100, 'pp': 20},
|
||||
'Confuse Ray': {'id': 109, 'power': 0, 'type': 'Ghost', 'accuracy': 100, 'pp': 10},
|
||||
'Withdraw': {'id': 110, 'power': 0, 'type': 'Water', 'accuracy': 100, 'pp': 40},
|
||||
'Defense Curl': {'id': 111, 'power': 0, 'type': 'Normal', 'accuracy': 100, 'pp': 40},
|
||||
'Barrier': {'id': 112, 'power': 0, 'type': 'Psychic', 'accuracy': 100, 'pp': 30},
|
||||
'Light Screen': {'id': 113, 'power': 0, 'type': 'Psychic', 'accuracy': 100, 'pp': 30},
|
||||
'Haze': {'id': 114, 'power': 0, 'type': 'Ice', 'accuracy': 100, 'pp': 30},
|
||||
'Reflect': {'id': 115, 'power': 0, 'type': 'Psychic', 'accuracy': 100, 'pp': 20},
|
||||
'Focus Energy': {'id': 116, 'power': 0, 'type': 'Normal', 'accuracy': 100, 'pp': 30},
|
||||
'Bide': {'id': 117, 'power': 0, 'type': 'Normal', 'accuracy': 100, 'pp': 10},
|
||||
'Metronome': {'id': 118, 'power': 0, 'type': 'Normal', 'accuracy': 100, 'pp': 10},
|
||||
'Mirror Move': {'id': 119, 'power': 0, 'type': 'Flying', 'accuracy': 100, 'pp': 20},
|
||||
'Selfdestruct': {'id': 120, 'power': 130, 'type': 'Normal', 'accuracy': 100, 'pp': 5},
|
||||
'Egg Bomb': {'id': 121, 'power': 100, 'type': 'Normal', 'accuracy': 75, 'pp': 10},
|
||||
'Lick': {'id': 122, 'power': 20, 'type': 'Ghost', 'accuracy': 100, 'pp': 30},
|
||||
'Smog': {'id': 123, 'power': 20, 'type': 'Poison', 'accuracy': 70, 'pp': 20},
|
||||
'Sludge': {'id': 124, 'power': 65, 'type': 'Poison', 'accuracy': 100, 'pp': 20},
|
||||
'Bone Club': {'id': 125, 'power': 65, 'type': 'Ground', 'accuracy': 85, 'pp': 20},
|
||||
'Fire Blast': {'id': 126, 'power': 120, 'type': 'Fire', 'accuracy': 85, 'pp': 5},
|
||||
'Waterfall': {'id': 127, 'power': 80, 'type': 'Water', 'accuracy': 100, 'pp': 15},
|
||||
'Clamp': {'id': 128, 'power': 35, 'type': 'Water', 'accuracy': 75, 'pp': 10},
|
||||
'Swift': {'id': 129, 'power': 60, 'type': 'Normal', 'accuracy': 100, 'pp': 20},
|
||||
'Skull Bash': {'id': 130, 'power': 100, 'type': 'Normal', 'accuracy': 100, 'pp': 15},
|
||||
'Spike Cannon': {'id': 131, 'power': 20, 'type': 'Normal', 'accuracy': 100, 'pp': 15},
|
||||
'Constrict': {'id': 132, 'power': 10, 'type': 'Normal', 'accuracy': 100, 'pp': 35},
|
||||
'Amnesia': {'id': 133, 'power': 0, 'type': 'Psychic', 'accuracy': 100, 'pp': 20},
|
||||
'Kinesis': {'id': 134, 'power': 0, 'type': 'Psychic', 'accuracy': 80, 'pp': 15},
|
||||
'Softboiled': {'id': 135, 'power': 0, 'type': 'Normal', 'accuracy': 100, 'pp': 10},
|
||||
'Hi Jump Kick': {'id': 136, 'power': 85, 'type': 'Fighting', 'accuracy': 90, 'pp': 20},
|
||||
'Glare': {'id': 137, 'power': 0, 'type': 'Normal', 'accuracy': 75, 'pp': 30},
|
||||
'Dream Eater': {'id': 138, 'power': 100, 'type': 'Psychic', 'accuracy': 100, 'pp': 15},
|
||||
'Poison Gas': {'id': 139, 'power': 0, 'type': 'Poison', 'accuracy': 55, 'pp': 40},
|
||||
'Barrage': {'id': 140, 'power': 15, 'type': 'Normal', 'accuracy': 85, 'pp': 20},
|
||||
'Leech Life': {'id': 141, 'power': 20, 'type': 'Bug', 'accuracy': 100, 'pp': 15},
|
||||
'Lovely Kiss': {'id': 142, 'power': 0, 'type': 'Normal', 'accuracy': 75, 'pp': 10},
|
||||
'Sky Attack': {'id': 143, 'power': 140, 'type': 'Flying', 'accuracy': 90, 'pp': 5},
|
||||
'Transform': {'id': 144, 'power': 0, 'type': 'Normal', 'accuracy': 100, 'pp': 10},
|
||||
'Bubble': {'id': 145, 'power': 20, 'type': 'Water', 'accuracy': 100, 'pp': 30},
|
||||
'Dizzy Punch': {'id': 146, 'power': 70, 'type': 'Normal', 'accuracy': 100, 'pp': 10},
|
||||
'Spore': {'id': 147, 'power': 0, 'type': 'Grass', 'accuracy': 100, 'pp': 15},
|
||||
'Flash': {'id': 148, 'power': 0, 'type': 'Normal', 'accuracy': 70, 'pp': 20},
|
||||
'Psywave': {'id': 149, 'power': 1, 'type': 'Psychic', 'accuracy': 80, 'pp': 15},
|
||||
'Splash': {'id': 150, 'power': 0, 'type': 'Normal', 'accuracy': 100, 'pp': 40},
|
||||
'Acid Armor': {'id': 151, 'power': 0, 'type': 'Poison', 'accuracy': 100, 'pp': 40},
|
||||
'Crabhammer': {'id': 152, 'power': 90, 'type': 'Water', 'accuracy': 85, 'pp': 10},
|
||||
'Explosion': {'id': 153, 'power': 170, 'type': 'Normal', 'accuracy': 100, 'pp': 5},
|
||||
'Fury Swipes': {'id': 154, 'power': 18, 'type': 'Normal', 'accuracy': 80, 'pp': 15},
|
||||
'Bonemerang': {'id': 155, 'power': 50, 'type': 'Ground', 'accuracy': 90, 'pp': 10},
|
||||
'Rest': {'id': 156, 'power': 0, 'type': 'Psychic', 'accuracy': 100, 'pp': 10},
|
||||
'Rock Slide': {'id': 157, 'power': 75, 'type': 'Rock', 'accuracy': 90, 'pp': 10},
|
||||
'Hyper Fang': {'id': 158, 'power': 80, 'type': 'Normal', 'accuracy': 90, 'pp': 15},
|
||||
'Sharpen': {'id': 159, 'power': 0, 'type': 'Normal', 'accuracy': 100, 'pp': 30},
|
||||
'Conversion': {'id': 160, 'power': 0, 'type': 'Normal', 'accuracy': 100, 'pp': 30},
|
||||
'Tri Attack': {'id': 161, 'power': 80, 'type': 'Normal', 'accuracy': 100, 'pp': 10},
|
||||
'Super Fang': {'id': 162, 'power': 1, 'type': 'Normal', 'accuracy': 90, 'pp': 10},
|
||||
'Slash': {'id': 163, 'power': 70, 'type': 'Normal', 'accuracy': 100, 'pp': 20},
|
||||
'Substitute': {'id': 164, 'power': 0, 'type': 'Normal', 'accuracy': 100, 'pp': 10},
|
||||
#'Struggle': {'id': 165, 'power': 50, 'type': 'Struggle_Type', 'accuracy': 100, 'pp': 10}
|
||||
'No Move': {'id': 0, 'power': 0, 'type': 'Typeless', 'accuracy': 0, 'pp': 0, 'effect': 0},
|
||||
'Pound': {'id': 1, 'power': 40, 'type': 'Normal', 'accuracy': 100, 'pp': 35, 'effect': 0},
|
||||
'Karate Chop': {'id': 2, 'power': 50, 'type': 'Normal', 'accuracy': 100, 'pp': 25, 'effect': 0},
|
||||
'Doubleslap': {'id': 3, 'power': 15, 'type': 'Normal', 'accuracy': 85, 'pp': 10, 'effect': 29},
|
||||
'Comet Punch': {'id': 4, 'power': 18, 'type': 'Normal', 'accuracy': 85, 'pp': 15, 'effect': 29},
|
||||
'Mega Punch': {'id': 5, 'power': 80, 'type': 'Normal', 'accuracy': 85, 'pp': 20, 'effect': 0},
|
||||
'Pay Day': {'id': 6, 'power': 40, 'type': 'Normal', 'accuracy': 100, 'pp': 20, 'effect': 16},
|
||||
'Fire Punch': {'id': 7, 'power': 75, 'type': 'Fire', 'accuracy': 100, 'pp': 15, 'effect': 4},
|
||||
'Ice Punch': {'id': 8, 'power': 75, 'type': 'Ice', 'accuracy': 100, 'pp': 15, 'effect': 5},
|
||||
'Thunderpunch': {'id': 9, 'power': 75, 'type': 'Electric', 'accuracy': 100, 'pp': 15, 'effect': 6},
|
||||
'Scratch': {'id': 10, 'power': 40, 'type': 'Normal', 'accuracy': 100, 'pp': 35, 'effect': 0},
|
||||
'Vicegrip': {'id': 11, 'power': 55, 'type': 'Normal', 'accuracy': 100, 'pp': 30, 'effect': 0},
|
||||
'Guillotine': {'id': 12, 'power': 1, 'type': 'Normal', 'accuracy': 30, 'pp': 5, 'effect': 38},
|
||||
'Razor Wind': {'id': 13, 'power': 80, 'type': 'Normal', 'accuracy': 75, 'pp': 10, 'effect': 39},
|
||||
'Swords Dance': {'id': 14, 'power': 0, 'type': 'Normal', 'accuracy': 100, 'pp': 30, 'effect': 50},
|
||||
'Cut': {'id': 15, 'power': 50, 'type': 'Normal', 'accuracy': 95, 'pp': 30, 'effect': 0},
|
||||
'Gust': {'id': 16, 'power': 40, 'type': 'Normal', 'accuracy': 100, 'pp': 35, 'effect': 0},
|
||||
'Wing Attack': {'id': 17, 'power': 35, 'type': 'Flying', 'accuracy': 100, 'pp': 35, 'effect': 0},
|
||||
'Whirlwind': {'id': 18, 'power': 0, 'type': 'Normal', 'accuracy': 85, 'pp': 20, 'effect': 28},
|
||||
'Fly': {'id': 19, 'power': 70, 'type': 'Flying', 'accuracy': 95, 'pp': 15, 'effect': 43},
|
||||
'Bind': {'id': 20, 'power': 15, 'type': 'Normal', 'accuracy': 75, 'pp': 20, 'effect': 42},
|
||||
'Slam': {'id': 21, 'power': 80, 'type': 'Normal', 'accuracy': 75, 'pp': 20, 'effect': 0},
|
||||
'Vine Whip': {'id': 22, 'power': 35, 'type': 'Grass', 'accuracy': 100, 'pp': 10, 'effect': 0},
|
||||
'Stomp': {'id': 23, 'power': 65, 'type': 'Normal', 'accuracy': 100, 'pp': 20, 'effect': 37},
|
||||
'Double Kick': {'id': 24, 'power': 30, 'type': 'Fighting', 'accuracy': 100, 'pp': 30, 'effect': 44},
|
||||
'Mega Kick': {'id': 25, 'power': 120, 'type': 'Normal', 'accuracy': 75, 'pp': 5, 'effect': 0},
|
||||
'Jump Kick': {'id': 26, 'power': 70, 'type': 'Fighting', 'accuracy': 95, 'pp': 25, 'effect': 45},
|
||||
'Rolling Kick': {'id': 27, 'power': 60, 'type': 'Fighting', 'accuracy': 85, 'pp': 15, 'effect': 37},
|
||||
'Sand Attack': {'id': 28, 'power': 0, 'type': 'Normal', 'accuracy': 100, 'pp': 15, 'effect': 22},
|
||||
'Headbutt': {'id': 29, 'power': 70, 'type': 'Normal', 'accuracy': 100, 'pp': 15, 'effect': 37},
|
||||
'Horn Attack': {'id': 30, 'power': 65, 'type': 'Normal', 'accuracy': 100, 'pp': 25, 'effect': 0},
|
||||
'Fury Attack': {'id': 31, 'power': 15, 'type': 'Normal', 'accuracy': 85, 'pp': 20, 'effect': 29},
|
||||
'Horn Drill': {'id': 32, 'power': 1, 'type': 'Normal', 'accuracy': 30, 'pp': 5, 'effect': 38},
|
||||
'Tackle': {'id': 33, 'power': 35, 'type': 'Normal', 'accuracy': 95, 'pp': 35, 'effect': 0},
|
||||
'Body Slam': {'id': 34, 'power': 85, 'type': 'Normal', 'accuracy': 100, 'pp': 15, 'effect': 36},
|
||||
'Wrap': {'id': 35, 'power': 15, 'type': 'Normal', 'accuracy': 85, 'pp': 20, 'effect': 42},
|
||||
'Take Down': {'id': 36, 'power': 90, 'type': 'Normal', 'accuracy': 85, 'pp': 20, 'effect': 48},
|
||||
'Thrash': {'id': 37, 'power': 90, 'type': 'Normal', 'accuracy': 100, 'pp': 20, 'effect': 27},
|
||||
'Double Edge': {'id': 38, 'power': 100, 'type': 'Normal', 'accuracy': 100, 'pp': 15, 'effect': 48},
|
||||
'Tail Whip': {'id': 39, 'power': 0, 'type': 'Normal', 'accuracy': 100, 'pp': 30, 'effect': 19},
|
||||
'Poison Sting': {'id': 40, 'power': 15, 'type': 'Poison', 'accuracy': 100, 'pp': 35, 'effect': 2},
|
||||
'Twineedle': {'id': 41, 'power': 25, 'type': 'Bug', 'accuracy': 100, 'pp': 20, 'effect': 77},
|
||||
'Pin Missile': {'id': 42, 'power': 14, 'type': 'Bug', 'accuracy': 85, 'pp': 20, 'effect': 29},
|
||||
'Leer': {'id': 43, 'power': 0, 'type': 'Normal', 'accuracy': 100, 'pp': 30, 'effect': 19},
|
||||
'Bite': {'id': 44, 'power': 60, 'type': 'Normal', 'accuracy': 100, 'pp': 25, 'effect': 31},
|
||||
'Growl': {'id': 45, 'power': 0, 'type': 'Normal', 'accuracy': 100, 'pp': 40, 'effect': 18},
|
||||
'Roar': {'id': 46, 'power': 0, 'type': 'Normal', 'accuracy': 100, 'pp': 20, 'effect': 28},
|
||||
'Sing': {'id': 47, 'power': 0, 'type': 'Normal', 'accuracy': 55, 'pp': 15, 'effect': 32},
|
||||
'Supersonic': {'id': 48, 'power': 0, 'type': 'Normal', 'accuracy': 55, 'pp': 20, 'effect': 49},
|
||||
'Sonicboom': {'id': 49, 'power': 1, 'type': 'Normal', 'accuracy': 90, 'pp': 20, 'effect': 41},
|
||||
'Disable': {'id': 50, 'power': 0, 'type': 'Normal', 'accuracy': 55, 'pp': 20, 'effect': 86},
|
||||
'Acid': {'id': 51, 'power': 40, 'type': 'Poison', 'accuracy': 100, 'pp': 30, 'effect': 69},
|
||||
'Ember': {'id': 52, 'power': 40, 'type': 'Fire', 'accuracy': 100, 'pp': 25, 'effect': 4},
|
||||
'Flamethrower': {'id': 53, 'power': 95, 'type': 'Fire', 'accuracy': 100, 'pp': 15, 'effect': 4},
|
||||
'Mist': {'id': 54, 'power': 0, 'type': 'Ice', 'accuracy': 100, 'pp': 30, 'effect': 46},
|
||||
'Water Gun': {'id': 55, 'power': 40, 'type': 'Water', 'accuracy': 100, 'pp': 25, 'effect': 0},
|
||||
'Hydro Pump': {'id': 56, 'power': 120, 'type': 'Water', 'accuracy': 80, 'pp': 5, 'effect': 0},
|
||||
'Surf': {'id': 57, 'power': 95, 'type': 'Water', 'accuracy': 100, 'pp': 15, 'effect': 0},
|
||||
'Ice Beam': {'id': 58, 'power': 95, 'type': 'Ice', 'accuracy': 100, 'pp': 10, 'effect': 5},
|
||||
'Blizzard': {'id': 59, 'power': 120, 'type': 'Ice', 'accuracy': 90, 'pp': 5, 'effect': 5},
|
||||
'Psybeam': {'id': 60, 'power': 65, 'type': 'Psychic', 'accuracy': 100, 'pp': 20, 'effect': 76},
|
||||
'Bubblebeam': {'id': 61, 'power': 65, 'type': 'Water', 'accuracy': 100, 'pp': 20, 'effect': 70},
|
||||
'Aurora Beam': {'id': 62, 'power': 65, 'type': 'Ice', 'accuracy': 100, 'pp': 20, 'effect': 68},
|
||||
'Hyper Beam': {'id': 63, 'power': 150, 'type': 'Normal', 'accuracy': 90, 'pp': 5, 'effect': 80},
|
||||
'Peck': {'id': 64, 'power': 35, 'type': 'Flying', 'accuracy': 100, 'pp': 35, 'effect': 0},
|
||||
'Drill Peck': {'id': 65, 'power': 80, 'type': 'Flying', 'accuracy': 100, 'pp': 20, 'effect': 0},
|
||||
'Submission': {'id': 66, 'power': 80, 'type': 'Fighting', 'accuracy': 80, 'pp': 25, 'effect': 48},
|
||||
'Low Kick': {'id': 67, 'power': 50, 'type': 'Fighting', 'accuracy': 90, 'pp': 20, 'effect': 37},
|
||||
'Counter': {'id': 68, 'power': 1, 'type': 'Fighting', 'accuracy': 100, 'pp': 20, 'effect': 0},
|
||||
'Seismic Toss': {'id': 69, 'power': 1, 'type': 'Fighting', 'accuracy': 100, 'pp': 20, 'effect': 41},
|
||||
'Strength': {'id': 70, 'power': 80, 'type': 'Normal', 'accuracy': 100, 'pp': 15, 'effect': 0},
|
||||
'Absorb': {'id': 71, 'power': 20, 'type': 'Grass', 'accuracy': 100, 'pp': 20, 'effect': 3},
|
||||
'Mega Drain': {'id': 72, 'power': 40, 'type': 'Grass', 'accuracy': 100, 'pp': 10, 'effect': 3},
|
||||
'Leech Seed': {'id': 73, 'power': 0, 'type': 'Grass', 'accuracy': 90, 'pp': 10, 'effect': 84},
|
||||
'Growth': {'id': 74, 'power': 0, 'type': 'Normal', 'accuracy': 100, 'pp': 40, 'effect': 13},
|
||||
'Razor Leaf': {'id': 75, 'power': 55, 'type': 'Grass', 'accuracy': 95, 'pp': 25, 'effect': 0},
|
||||
'Solarbeam': {'id': 76, 'power': 120, 'type': 'Grass', 'accuracy': 100, 'pp': 10, 'effect': 39},
|
||||
'Poisonpowder': {'id': 77, 'power': 0, 'type': 'Poison', 'accuracy': 75, 'pp': 35, 'effect': 66},
|
||||
'Stun Spore': {'id': 78, 'power': 0, 'type': 'Grass', 'accuracy': 75, 'pp': 30, 'effect': 67},
|
||||
'Sleep Powder': {'id': 79, 'power': 0, 'type': 'Grass', 'accuracy': 75, 'pp': 15, 'effect': 32},
|
||||
'Petal Dance': {'id': 80, 'power': 70, 'type': 'Grass', 'accuracy': 100, 'pp': 20, 'effect': 27},
|
||||
'String Shot': {'id': 81, 'power': 0, 'type': 'Bug', 'accuracy': 95, 'pp': 40, 'effect': 20},
|
||||
'Dragon Rage': {'id': 82, 'power': 1, 'type': 'Dragon', 'accuracy': 100, 'pp': 10, 'effect': 41},
|
||||
'Fire Spin': {'id': 83, 'power': 15, 'type': 'Fire', 'accuracy': 70, 'pp': 15, 'effect': 42},
|
||||
'Thundershock': {'id': 84, 'power': 40, 'type': 'Electric', 'accuracy': 100, 'pp': 30, 'effect': 6},
|
||||
'Thunderbolt': {'id': 85, 'power': 95, 'type': 'Electric', 'accuracy': 100, 'pp': 15, 'effect': 6},
|
||||
'Thunder Wave': {'id': 86, 'power': 0, 'type': 'Electric', 'accuracy': 100, 'pp': 20, 'effect': 67},
|
||||
'Thunder': {'id': 87, 'power': 120, 'type': 'Electric', 'accuracy': 70, 'pp': 10, 'effect': 6},
|
||||
'Rock Throw': {'id': 88, 'power': 50, 'type': 'Rock', 'accuracy': 65, 'pp': 15, 'effect': 0},
|
||||
'Earthquake': {'id': 89, 'power': 100, 'type': 'Ground', 'accuracy': 100, 'pp': 10, 'effect': 0},
|
||||
'Fissure': {'id': 90, 'power': 1, 'type': 'Ground', 'accuracy': 30, 'pp': 5, 'effect': 38},
|
||||
'Dig': {'id': 91, 'power': 100, 'type': 'Ground', 'accuracy': 100, 'pp': 10, 'effect': 39},
|
||||
'Toxic': {'id': 92, 'power': 0, 'type': 'Poison', 'accuracy': 85, 'pp': 10, 'effect': 66},
|
||||
'Confusion': {'id': 93, 'power': 50, 'type': 'Psychic', 'accuracy': 100, 'pp': 25, 'effect': 76},
|
||||
'Psychic': {'id': 94, 'power': 90, 'type': 'Psychic', 'accuracy': 100, 'pp': 10, 'effect': 71},
|
||||
'Hypnosis': {'id': 95, 'power': 0, 'type': 'Psychic', 'accuracy': 60, 'pp': 20, 'effect': 32},
|
||||
'Meditate': {'id': 96, 'power': 0, 'type': 'Psychic', 'accuracy': 100, 'pp': 40, 'effect': 10},
|
||||
'Agility': {'id': 97, 'power': 0, 'type': 'Psychic', 'accuracy': 100, 'pp': 30, 'effect': 52},
|
||||
'Quick Attack': {'id': 98, 'power': 40, 'type': 'Normal', 'accuracy': 100, 'pp': 30, 'effect': 0},
|
||||
'Rage': {'id': 99, 'power': 20, 'type': 'Normal', 'accuracy': 100, 'pp': 20, 'effect': 81},
|
||||
'Teleport': {'id': 100, 'power': 0, 'type': 'Psychic', 'accuracy': 100, 'pp': 20, 'effect': 28},
|
||||
'Night Shade': {'id': 101, 'power': 0, 'type': 'Ghost', 'accuracy': 100, 'pp': 15, 'effect': 41},
|
||||
'Mimic': {'id': 102, 'power': 0, 'type': 'Normal', 'accuracy': 100, 'pp': 10, 'effect': 82},
|
||||
'Screech': {'id': 103, 'power': 0, 'type': 'Normal', 'accuracy': 85, 'pp': 40, 'effect': 59},
|
||||
'Double Team': {'id': 104, 'power': 0, 'type': 'Normal', 'accuracy': 100, 'pp': 15, 'effect': 15},
|
||||
'Recover': {'id': 105, 'power': 0, 'type': 'Normal', 'accuracy': 100, 'pp': 20, 'effect': 56},
|
||||
'Harden': {'id': 106, 'power': 0, 'type': 'Normal', 'accuracy': 100, 'pp': 30, 'effect': 11},
|
||||
'Minimize': {'id': 107, 'power': 0, 'type': 'Normal', 'accuracy': 100, 'pp': 20, 'effect': 15},
|
||||
'Smokescreen': {'id': 108, 'power': 0, 'type': 'Normal', 'accuracy': 100, 'pp': 20, 'effect': 22},
|
||||
'Confuse Ray': {'id': 109, 'power': 0, 'type': 'Ghost', 'accuracy': 100, 'pp': 10, 'effect': 49},
|
||||
'Withdraw': {'id': 110, 'power': 0, 'type': 'Water', 'accuracy': 100, 'pp': 40, 'effect': 11},
|
||||
'Defense Curl': {'id': 111, 'power': 0, 'type': 'Normal', 'accuracy': 100, 'pp': 40, 'effect': 11},
|
||||
'Barrier': {'id': 112, 'power': 0, 'type': 'Psychic', 'accuracy': 100, 'pp': 30, 'effect': 51},
|
||||
'Light Screen': {'id': 113, 'power': 0, 'type': 'Psychic', 'accuracy': 100, 'pp': 30, 'effect': 64},
|
||||
'Haze': {'id': 114, 'power': 0, 'type': 'Ice', 'accuracy': 100, 'pp': 30, 'effect': 25},
|
||||
'Reflect': {'id': 115, 'power': 0, 'type': 'Psychic', 'accuracy': 100, 'pp': 20, 'effect': 65},
|
||||
'Focus Energy': {'id': 116, 'power': 0, 'type': 'Normal', 'accuracy': 100, 'pp': 30, 'effect': 47},
|
||||
'Bide': {'id': 117, 'power': 0, 'type': 'Normal', 'accuracy': 100, 'pp': 10, 'effect': 26},
|
||||
'Metronome': {'id': 118, 'power': 0, 'type': 'Normal', 'accuracy': 100, 'pp': 10, 'effect': 83},
|
||||
'Mirror Move': {'id': 119, 'power': 0, 'type': 'Flying', 'accuracy': 100, 'pp': 20, 'effect': 9},
|
||||
'Selfdestruct': {'id': 120, 'power': 130, 'type': 'Normal', 'accuracy': 100, 'pp': 5, 'effect': 7},
|
||||
'Egg Bomb': {'id': 121, 'power': 100, 'type': 'Normal', 'accuracy': 75, 'pp': 10, 'effect': 0},
|
||||
'Lick': {'id': 122, 'power': 20, 'type': 'Ghost', 'accuracy': 100, 'pp': 30, 'effect': 36},
|
||||
'Smog': {'id': 123, 'power': 20, 'type': 'Poison', 'accuracy': 70, 'pp': 20, 'effect': 33},
|
||||
'Sludge': {'id': 124, 'power': 65, 'type': 'Poison', 'accuracy': 100, 'pp': 20, 'effect': 33},
|
||||
'Bone Club': {'id': 125, 'power': 65, 'type': 'Ground', 'accuracy': 85, 'pp': 20, 'effect': 31},
|
||||
'Fire Blast': {'id': 126, 'power': 120, 'type': 'Fire', 'accuracy': 85, 'pp': 5, 'effect': 34},
|
||||
'Waterfall': {'id': 127, 'power': 80, 'type': 'Water', 'accuracy': 100, 'pp': 15, 'effect': 0},
|
||||
'Clamp': {'id': 128, 'power': 35, 'type': 'Water', 'accuracy': 75, 'pp': 10, 'effect': 42},
|
||||
'Swift': {'id': 129, 'power': 60, 'type': 'Normal', 'accuracy': 100, 'pp': 20, 'effect': 17},
|
||||
'Skull Bash': {'id': 130, 'power': 100, 'type': 'Normal', 'accuracy': 100, 'pp': 15, 'effect': 39},
|
||||
'Spike Cannon': {'id': 131, 'power': 20, 'type': 'Normal', 'accuracy': 100, 'pp': 15, 'effect': 29},
|
||||
'Constrict': {'id': 132, 'power': 10, 'type': 'Normal', 'accuracy': 100, 'pp': 35, 'effect': 70},
|
||||
'Amnesia': {'id': 133, 'power': 0, 'type': 'Psychic', 'accuracy': 100, 'pp': 20, 'effect': 53},
|
||||
'Kinesis': {'id': 134, 'power': 0, 'type': 'Psychic', 'accuracy': 80, 'pp': 15, 'effect': 22},
|
||||
'Softboiled': {'id': 135, 'power': 0, 'type': 'Normal', 'accuracy': 100, 'pp': 10, 'effect': 56},
|
||||
'Hi Jump Kick': {'id': 136, 'power': 85, 'type': 'Fighting', 'accuracy': 90, 'pp': 20, 'effect': 45},
|
||||
'Glare': {'id': 137, 'power': 0, 'type': 'Normal', 'accuracy': 75, 'pp': 30, 'effect': 67},
|
||||
'Dream Eater': {'id': 138, 'power': 100, 'type': 'Psychic', 'accuracy': 100, 'pp': 15, 'effect': 8},
|
||||
'Poison Gas': {'id': 139, 'power': 0, 'type': 'Poison', 'accuracy': 55, 'pp': 40, 'effect': 66},
|
||||
'Barrage': {'id': 140, 'power': 15, 'type': 'Normal', 'accuracy': 85, 'pp': 20, 'effect': 29},
|
||||
'Leech Life': {'id': 141, 'power': 20, 'type': 'Bug', 'accuracy': 100, 'pp': 15, 'effect': 3},
|
||||
'Lovely Kiss': {'id': 142, 'power': 0, 'type': 'Normal', 'accuracy': 75, 'pp': 10, 'effect': 32},
|
||||
'Sky Attack': {'id': 143, 'power': 140, 'type': 'Flying', 'accuracy': 90, 'pp': 5, 'effect': 39},
|
||||
'Transform': {'id': 144, 'power': 0, 'type': 'Normal', 'accuracy': 100, 'pp': 10, 'effect': 57},
|
||||
'Bubble': {'id': 145, 'power': 20, 'type': 'Water', 'accuracy': 100, 'pp': 30, 'effect': 70},
|
||||
'Dizzy Punch': {'id': 146, 'power': 70, 'type': 'Normal', 'accuracy': 100, 'pp': 10, 'effect': 0},
|
||||
'Spore': {'id': 147, 'power': 0, 'type': 'Grass', 'accuracy': 100, 'pp': 15, 'effect': 32},
|
||||
'Flash': {'id': 148, 'power': 0, 'type': 'Normal', 'accuracy': 70, 'pp': 20, 'effect': 22},
|
||||
'Psywave': {'id': 149, 'power': 1, 'type': 'Psychic', 'accuracy': 80, 'pp': 15, 'effect': 41},
|
||||
'Splash': {'id': 150, 'power': 0, 'type': 'Normal', 'accuracy': 100, 'pp': 40, 'effect': 85},
|
||||
'Acid Armor': {'id': 151, 'power': 0, 'type': 'Poison', 'accuracy': 100, 'pp': 40, 'effect': 51},
|
||||
'Crabhammer': {'id': 152, 'power': 90, 'type': 'Water', 'accuracy': 85, 'pp': 10, 'effect': 0},
|
||||
'Explosion': {'id': 153, 'power': 170, 'type': 'Normal', 'accuracy': 100, 'pp': 5, 'effect': 7},
|
||||
'Fury Swipes': {'id': 154, 'power': 18, 'type': 'Normal', 'accuracy': 80, 'pp': 15, 'effect': 29},
|
||||
'Bonemerang': {'id': 155, 'power': 50, 'type': 'Ground', 'accuracy': 90, 'pp': 10, 'effect': 44},
|
||||
'Rest': {'id': 156, 'power': 0, 'type': 'Psychic', 'accuracy': 100, 'pp': 10, 'effect': 56},
|
||||
'Rock Slide': {'id': 157, 'power': 75, 'type': 'Rock', 'accuracy': 90, 'pp': 10, 'effect': 0},
|
||||
'Hyper Fang': {'id': 158, 'power': 80, 'type': 'Normal', 'accuracy': 90, 'pp': 15, 'effect': 31},
|
||||
'Sharpen': {'id': 159, 'power': 0, 'type': 'Normal', 'accuracy': 100, 'pp': 30, 'effect': 10},
|
||||
'Conversion': {'id': 160, 'power': 0, 'type': 'Normal', 'accuracy': 100, 'pp': 30, 'effect': 24},
|
||||
'Tri Attack': {'id': 161, 'power': 80, 'type': 'Normal', 'accuracy': 100, 'pp': 10, 'effect': 0},
|
||||
'Super Fang': {'id': 162, 'power': 1, 'type': 'Normal', 'accuracy': 90, 'pp': 10, 'effect': 40},
|
||||
'Slash': {'id': 163, 'power': 70, 'type': 'Normal', 'accuracy': 100, 'pp': 20, 'effect': 0},
|
||||
'Substitute': {'id': 164, 'power': 0, 'type': 'Normal', 'accuracy': 100, 'pp': 10, 'effect': 79}
|
||||
#'Struggle': {'id': 165, 'power': 50, 'type': 'Struggle_Type', 'accuracy': 100, 'pp': 10, 'effect': 48}
|
||||
}
|
||||
|
||||
encounter_tables = {'Wild_Super_Rod_A': 2, 'Wild_Super_Rod_B': 2, 'Wild_Super_Rod_C': 3, 'Wild_Super_Rod_D': 2,
|
||||
|
@ -1204,6 +1204,29 @@ tm_moves = [
|
|||
'Selfdestruct', 'Egg Bomb', 'Fire Blast', 'Swift', 'Skull Bash', 'Softboiled', 'Dream Eater', 'Sky Attack', 'Rest',
|
||||
'Thunder Wave', 'Psywave', 'Explosion', 'Rock Slide', 'Tri Attack', 'Substitute'
|
||||
]
|
||||
#['No Move', 'Pound', 'Karate Chop', 'Doubleslap', 'Comet Punch', 'Fire Punch', 'Ice Punch', 'Thunderpunch', 'Scratch',
|
||||
# 'Vicegrip', 'Guillotine', 'Cut', 'Gust', 'Wing Attack', 'Fly', 'Bind', 'Slam', 'Vine Whip', 'Stomp', 'Double Kick', 'Jump Kick',
|
||||
# 'Rolling Kick', 'Sand Attack', 'Headbutt', 'Horn Attack', 'Fury Attack', 'Tackle', 'Wrap', 'Thrash', 'Tail Whip', 'Poison Sting',
|
||||
# 'Twineedle', 'Pin Missile', 'Leer', 'Bite', 'Growl', 'Roar', 'Sing', 'Supersonic', 'Sonicboom', 'Disable', 'Acid', 'Ember', 'Flamethrower',
|
||||
# 'Mist', 'Hydro Pump', 'Surf', 'Psybeam', 'Aurora Beam', 'Peck', 'Drill Peck', 'Low Kick', 'Strength', 'Absorb', 'Leech Seed', 'Growth',
|
||||
# 'Razor Leaf', 'Poisonpowder', 'Stun Spore', 'Sleep Powder', 'Petal Dance', 'String Shot', 'Fire Spin', 'Thundershock', 'Rock Throw', 'Confusion',
|
||||
# 'Hypnosis', 'Meditate', 'Agility', 'Quick Attack', 'Night Shade', 'Screech', 'Recover', 'Harden', 'Minimize', 'Smokescreen', 'Confuse Ray', 'Withdraw',
|
||||
# 'Defense Curl', 'Barrier', 'Light Screen', 'Haze', 'Focus Energy', 'Mirror Move', 'Lick', 'Smog', 'Sludge', 'Bone Club', 'Waterfall', 'Clamp', 'Spike Cannon',
|
||||
# 'Constrict', 'Amnesia', 'Kinesis', 'Hi Jump Kick', 'Glare', 'Poison Gas', 'Barrage', 'Leech Life', 'Lovely Kiss', 'Transform', 'Bubble', 'Dizzy Punch', 'Spore', 'Flash',
|
||||
# 'Splash', 'Acid Armor', 'Crabhammer', 'Fury Swipes', 'Bonemerang', 'Hyper Fang', 'Sharpen', 'Conversion', 'Super Fang', 'Slash']
|
||||
|
||||
# print([i for i in list(moves.keys()) if i not in tm_moves])
|
||||
# filler_moves = [
|
||||
# "Razor Wind", "Whirlwind", "Counter", "Teleport", "Bide", "Skull Bash", "Sky Attack", "Psywave",
|
||||
# "Pound", "Karate Chop", "Doubleslap", "Comet Punch", "Scratch", "Vicegrip", "Gust", "Wing Attack", "Bind",
|
||||
# "Vine Whip", "Sand Attack", "Fury Attack", "Tackle", "Wrap", "Tail Whip", "Poison Sting", "Twineedle",
|
||||
# "Leer", "Growl", "Roar", "Sing", "Supersonic", "Sonicboom", "Disable", "Acid", "Ember", "Mist", "Peck", "Absorb",
|
||||
# "Growth", "Poisonpowder", "String Shot", "Meditate", "Agility", "Screech", "Double Team", "Harden", "Minimize",
|
||||
# "Smokescreen", "Confuse Ray", "Withdraw", "Defense Curl", "Barrier", "Light Screen", "Haze", "Reflect",
|
||||
# "Focus Energy", "Lick", "Smog", "Clamp", "Spike Cannon", "Constrict"
|
||||
#
|
||||
# ]
|
||||
|
||||
|
||||
first_stage_pokemon = [pokemon for pokemon in pokemon_data.keys() if pokemon not in evolves_from]
|
||||
legendary_pokemon = ["Articuno", "Zapdos", "Moltres", "Mewtwo", "Mew"]
|
||||
|
|
|
@ -23,11 +23,12 @@ def create_regions(multiworld: MultiWorld, player: int):
|
|||
locations_per_region.setdefault(location.region, [])
|
||||
if location.inclusion(multiworld, player):
|
||||
locations_per_region[location.region].append(PokemonRBLocation(player, location.name, location.address,
|
||||
location.rom_address))
|
||||
location.rom_address, location.type))
|
||||
regions = [
|
||||
create_region(multiworld, player, "Menu", locations_per_region),
|
||||
create_region(multiworld, player, "Anywhere", locations_per_region),
|
||||
create_region(multiworld, player, "Fossil", locations_per_region),
|
||||
create_region(multiworld, player, "Pokedex", locations_per_region),
|
||||
create_region(multiworld, player, "Pallet Town", locations_per_region),
|
||||
create_region(multiworld, player, "Route 1", locations_per_region),
|
||||
create_region(multiworld, player, "Viridian City", locations_per_region),
|
||||
|
@ -88,6 +89,7 @@ def create_regions(multiworld: MultiWorld, player: int):
|
|||
create_region(multiworld, player, "Route 8", locations_per_region),
|
||||
create_region(multiworld, player, "Route 8 Grass", locations_per_region),
|
||||
create_region(multiworld, player, "Celadon City", locations_per_region),
|
||||
create_region(multiworld, player, "Celadon Game Corner", locations_per_region),
|
||||
create_region(multiworld, player, "Celadon Prize Corner", locations_per_region),
|
||||
create_region(multiworld, player, "Celadon Gym", locations_per_region),
|
||||
create_region(multiworld, player, "Route 16", locations_per_region),
|
||||
|
@ -148,6 +150,7 @@ def create_regions(multiworld: MultiWorld, player: int):
|
|||
multiworld.regions += regions
|
||||
connect(multiworld, player, "Menu", "Anywhere", one_way=True)
|
||||
connect(multiworld, player, "Menu", "Pallet Town", one_way=True)
|
||||
connect(multiworld, player, "Menu", "Pokedex", one_way=True)
|
||||
connect(multiworld, player, "Menu", "Fossil", lambda state: state.pokemon_rb_fossil_checks(
|
||||
state.multiworld.second_fossil_check_condition[player].value, player), one_way=True)
|
||||
connect(multiworld, player, "Pallet Town", "Route 1")
|
||||
|
@ -220,6 +223,7 @@ def create_regions(multiworld: MultiWorld, player: int):
|
|||
connect(multiworld, player, "Route 8", "Route 8 Grass", lambda state: state.pokemon_rb_can_cut(player), one_way=True)
|
||||
connect(multiworld, player, "Route 7", "Celadon City")
|
||||
connect(multiworld, player, "Celadon City", "Celadon Gym", lambda state: state.pokemon_rb_can_cut(player), one_way=True)
|
||||
connect(multiworld, player, "Celadon City", "Celadon Game Corner")
|
||||
connect(multiworld, player, "Celadon City", "Celadon Prize Corner")
|
||||
connect(multiworld, player, "Celadon City", "Route 16")
|
||||
connect(multiworld, player, "Route 16", "Route 16 North", lambda state: state.pokemon_rb_can_cut(player), one_way=True)
|
||||
|
|
|
@ -0,0 +1,294 @@
|
|||
from .rom_addresses import rom_addresses
|
||||
|
||||
disallowed1F = [[2, 2], [3, 2], [1, 8], [2, 8], [7, 7], [8, 7], [10, 4], [11, 4], [11, 12],
|
||||
[11, 13], [16, 10], [17, 10], [18, 10], [16, 12], [17, 12], [18, 12]]
|
||||
disallowed2F = [[16, 2], [17, 2], [18, 2], [15, 5], [15, 6], [10, 10], [11, 10], [12, 10], [7, 14], [8, 14], [1, 15],
|
||||
[13, 15], [13, 16], [1, 12], [1, 10], [3, 5], [3, 6], [5, 6], [5, 7], [5, 8], [1, 2], [1, 3], [1, 4],
|
||||
[11, 1]]
|
||||
|
||||
|
||||
def randomize_rock_tunnel(data, random):
|
||||
|
||||
seed = random.randint(0, 999999999999999999)
|
||||
random.seed(seed)
|
||||
|
||||
map1f = []
|
||||
map2f = []
|
||||
|
||||
address = rom_addresses["Map_Rock_Tunnel1F"]
|
||||
for y in range(0, 18):
|
||||
row = []
|
||||
for x in range(0, 20):
|
||||
row.append(data[address])
|
||||
address += 1
|
||||
map1f.append(row)
|
||||
|
||||
address = rom_addresses["Map_Rock_TunnelB1F"]
|
||||
for y in range(0, 18):
|
||||
row = []
|
||||
for x in range(0, 20):
|
||||
row.append(data[address])
|
||||
address += 1
|
||||
map2f.append(row)
|
||||
|
||||
current_map = map1f
|
||||
|
||||
def floor(x, y):
|
||||
current_map[y][x] = 1
|
||||
|
||||
def wide(x, y):
|
||||
current_map[y][x] = 32
|
||||
current_map[y][x + 1] = 34
|
||||
|
||||
def tall(x, y):
|
||||
current_map[y][x] = 23
|
||||
current_map[y + 1][x] = 31
|
||||
|
||||
def single(x, y):
|
||||
current_map[y][x] = 2
|
||||
|
||||
# 0 = top left, 1 = middle, 2 = top right, 3 = bottom right
|
||||
entrance_c = random.choice([0, 1, 2])
|
||||
exit_c = [0, 1, 3]
|
||||
if entrance_c == 2:
|
||||
exit_c.remove(1)
|
||||
else:
|
||||
exit_c.remove(entrance_c)
|
||||
exit_c = random.choice(exit_c)
|
||||
remaining = [i for i in [0, 1, 2, 3] if i not in [entrance_c, exit_c]]
|
||||
|
||||
if entrance_c == 0:
|
||||
floor(6, 3)
|
||||
floor(6, 4)
|
||||
tall(random.randint(8, 10), 2)
|
||||
wide(4, random.randint(5, 7))
|
||||
wide(1, random.choice([5, 6, 7, 9]))
|
||||
elif entrance_c == 1:
|
||||
if remaining == [0, 2] or random.randint(0, 1):
|
||||
tall(random.randint(8, 10), 2)
|
||||
floor(7, 4)
|
||||
floor(8, 4)
|
||||
else:
|
||||
tall(random.randint(11, 12), 5)
|
||||
floor(9, 5)
|
||||
floor(9, 6)
|
||||
elif entrance_c == 2:
|
||||
floor(16, 2)
|
||||
floor(16, 3)
|
||||
if remaining == [1, 3]:
|
||||
wide(17, 4)
|
||||
else:
|
||||
tall(random.randint(11, 17), random.choice([2, 5]))
|
||||
|
||||
if exit_c == 0:
|
||||
r = random.sample([0, 1, 2], 2)
|
||||
if 0 in r:
|
||||
floor(1, 11)
|
||||
floor(2, 11)
|
||||
if 1 in r:
|
||||
floor(3, 11)
|
||||
floor(4, 11)
|
||||
if 2 in r:
|
||||
floor(5, 11)
|
||||
floor(6, 11)
|
||||
elif exit_c == 1 or (exit_c == 3 and entrance_c == 0):
|
||||
r = random.sample([1, 3, 5, 7], random.randint(1, 2))
|
||||
for i in r:
|
||||
floor(i, 11)
|
||||
floor(i + 1, 11)
|
||||
if exit_c != 3:
|
||||
tall(random.choice([9, 10, 12]), 12)
|
||||
|
||||
# 0 = top left, 1 = middle, 2 = top right, 3 = bottom right
|
||||
# [0, 1] [0, 2] [1, 2] [1, 3], [2, 3]
|
||||
if remaining[0] == 1:
|
||||
floor(9, 5)
|
||||
floor(9, 6)
|
||||
|
||||
if remaining == [0, 2]:
|
||||
if random.randint(0, 1):
|
||||
tall(9, 4)
|
||||
floor(9, 6)
|
||||
floor(9, 7)
|
||||
else:
|
||||
floor(10, 7)
|
||||
floor(11, 7)
|
||||
|
||||
if remaining == [1, 2]:
|
||||
floor(16, 2)
|
||||
floor(16, 3)
|
||||
tall(random.randint(11, 17), random.choice([2, 5]))
|
||||
if remaining in [[1, 3], [2, 3]]:
|
||||
r = round(random.triangular(0, 3, 0))
|
||||
floor(12 + (r * 2), 7)
|
||||
if r < 3:
|
||||
floor(13 + (r * 2), 7)
|
||||
if remaining == [1, 3]:
|
||||
wide(10, random.choice([3, 5]))
|
||||
|
||||
if remaining != [0, 1] and exit_c != 1:
|
||||
wide(7, 6)
|
||||
|
||||
if entrance_c != 0:
|
||||
if random.randint(0, 1):
|
||||
wide(4, random.randint(4, 7))
|
||||
else:
|
||||
wide(1, random.choice([5, 6, 7, 9]))
|
||||
|
||||
current_map = map2f
|
||||
|
||||
if 3 in remaining:
|
||||
c = random.choice([entrance_c, exit_c])
|
||||
else:
|
||||
c = random.choice(remaining)
|
||||
|
||||
# 0 = top right, 1 = middle, 2 = bottom right, 3 = top left
|
||||
if c in [0, 1]:
|
||||
if random.randint(0, 2):
|
||||
tall(random.choice([2, 4]), 5)
|
||||
r = random.choice([1, 3, 7, 9, 11])
|
||||
floor(3 if r < 11 else random.randint(1, 2), r)
|
||||
floor(3 if r < 11 else random.randint(1, 2), r + 1)
|
||||
if random.randint(0, 2):
|
||||
tall(random.randint(6, 7), 7)
|
||||
r = random.choice([1, 3, 5, 9])
|
||||
floor(6, r)
|
||||
floor(6, r + 1)
|
||||
if random.randint(0, 2):
|
||||
wide(7, 15)
|
||||
r = random.randint(0, 4)
|
||||
if r == 0:
|
||||
floor(9, 14)
|
||||
floor(10, 14)
|
||||
elif r == 1:
|
||||
floor(11, 14)
|
||||
floor(12, 14)
|
||||
elif r == 2:
|
||||
floor(13, 13)
|
||||
floor(13, 14)
|
||||
elif r == 3:
|
||||
floor(13, 11)
|
||||
floor(13, 12)
|
||||
elif r == 4:
|
||||
floor(13, 10)
|
||||
floor(14, 10)
|
||||
if c == 0:
|
||||
tall(random.randint(9, 10), 5)
|
||||
if random.randint(0, 1):
|
||||
floor(10, 7)
|
||||
floor(11, 7)
|
||||
tall(random.randint(12, 17), 8)
|
||||
else:
|
||||
floor(12, 5)
|
||||
floor(12, 6)
|
||||
wide(13, random.randint(4, 5))
|
||||
wide(17, random.randint(3, 5))
|
||||
r = random.choice([1, 3])
|
||||
floor(12, r)
|
||||
floor(12, + 1)
|
||||
|
||||
elif c == 2:
|
||||
r = random.randint(0, 6)
|
||||
if r == 0:
|
||||
floor(12, 1)
|
||||
floor(12, 2)
|
||||
elif r == 1:
|
||||
floor(12, 3)
|
||||
floor(12, 4)
|
||||
elif r == 2:
|
||||
floor(12, 5)
|
||||
floor(12, 6)
|
||||
elif r == 3:
|
||||
floor(10, 7)
|
||||
floor(11, 7)
|
||||
elif r == 4:
|
||||
floor(9, 7)
|
||||
floor(9, 8)
|
||||
elif r == 5:
|
||||
floor(9, 9)
|
||||
floor(9, 10)
|
||||
elif r == 6:
|
||||
floor(8, 11)
|
||||
floor(9, 11)
|
||||
if r < 2 or (r in [2, 3] and random.randint(0, 1)):
|
||||
wide(7, random.randint(6, 7))
|
||||
elif r in [2, 3]:
|
||||
tall(random.randint(9, 10), 5)
|
||||
else:
|
||||
tall(random.randint(6, 7), 7)
|
||||
r = random.randint(r, 6)
|
||||
if r == 0:
|
||||
#early block
|
||||
wide(13, random.randint(2, 5))
|
||||
tall(random.randint(14, 15), 1)
|
||||
elif r == 1:
|
||||
if random.randint(0, 1):
|
||||
tall(16, 5)
|
||||
tall(random.choice([14, 15, 17]), 1)
|
||||
else:
|
||||
wide(16, random.randint(6,8))
|
||||
single(18, 7)
|
||||
elif r == 2:
|
||||
tall(random.randint(12, 16), 8)
|
||||
elif r == 3:
|
||||
wide(10, 9)
|
||||
single(12, 9)
|
||||
elif r == 4:
|
||||
wide(10, random.randint(11, 12))
|
||||
single(12, random.randint(11, 12))
|
||||
elif r == 5:
|
||||
tall(random.randint(8, 10), 12)
|
||||
elif r == 6:
|
||||
wide(7, 15)
|
||||
r = random.randint(r, 6)
|
||||
if r == 6:
|
||||
#late open
|
||||
r2 = random.randint(0, 2)
|
||||
floor(1 + (r2 * 2), 14)
|
||||
floor(2 + (r2 * 2), 14)
|
||||
elif r == 5:
|
||||
floor(6, 12)
|
||||
floor(6, 13)
|
||||
elif r == 4:
|
||||
if random.randint(0, 1):
|
||||
floor(6, 11)
|
||||
floor(7, 11)
|
||||
else:
|
||||
floor(8, 11)
|
||||
floor(9, 11)
|
||||
elif r == 3:
|
||||
floor(9, 9)
|
||||
floor(9, 10)
|
||||
elif r < 3:
|
||||
single(9, 7)
|
||||
floor(9, 8)
|
||||
|
||||
def check_addable_block(check_map, disallowed):
|
||||
if check_map[y][x] == 1 and [x, y] not in disallowed:
|
||||
i = 0
|
||||
for xx in range(x-1, x+2):
|
||||
for yy in range(y-1, y+2):
|
||||
if check_map[yy][xx] == 1:
|
||||
i += 1
|
||||
if i >= 8:
|
||||
single(x, y)
|
||||
|
||||
for _ in range(100):
|
||||
y = random.randint(1, 16)
|
||||
x = random.randint(1, 18)
|
||||
current_map = map1f
|
||||
check_addable_block(map1f, disallowed1F)
|
||||
current_map = map2f
|
||||
check_addable_block(map2f, disallowed2F)
|
||||
|
||||
address = rom_addresses["Map_Rock_Tunnel1F"]
|
||||
for y in map1f:
|
||||
for x in y:
|
||||
data[address] = x
|
||||
address += 1
|
||||
address = rom_addresses["Map_Rock_TunnelB1F"]
|
||||
for y in map2f:
|
||||
for x in y:
|
||||
data[address] = x
|
||||
address += 1
|
||||
return seed
|
|
@ -8,6 +8,7 @@ from .text import encode_text
|
|||
from .rom_addresses import rom_addresses
|
||||
from .locations import location_data
|
||||
from .items import item_table
|
||||
from .rock_tunnel import randomize_rock_tunnel
|
||||
import worlds.pokemon_rb.poke_data as poke_data
|
||||
|
||||
|
||||
|
@ -28,15 +29,15 @@ def filter_moves(moves, type, random):
|
|||
return ret
|
||||
|
||||
|
||||
def get_move(moves, chances, random, starting_move=False):
|
||||
def get_move(local_move_data, moves, chances, random, starting_move=False):
|
||||
type = choose_forced_type(chances, random)
|
||||
filtered_moves = filter_moves(moves, type, random)
|
||||
for move in filtered_moves:
|
||||
if poke_data.moves[move]["accuracy"] > 80 and poke_data.moves[move]["power"] > 0 or not starting_move:
|
||||
if local_move_data[move]["accuracy"] > 80 and local_move_data[move]["power"] > 0 or not starting_move:
|
||||
moves.remove(move)
|
||||
return move
|
||||
else:
|
||||
return get_move(moves, [], random, starting_move)
|
||||
return get_move(local_move_data, moves, [], random, starting_move)
|
||||
|
||||
|
||||
def get_encounter_slots(self):
|
||||
|
@ -75,6 +76,42 @@ def randomize_pokemon(self, mon, mons_list, randomize_type, random):
|
|||
return mon
|
||||
|
||||
|
||||
def set_mon_palettes(self, random, data):
|
||||
if self.multiworld.randomize_pokemon_palettes[self.player] == "vanilla":
|
||||
return
|
||||
pallet_map = {
|
||||
"Poison": 0x0F,
|
||||
"Normal": 0x10,
|
||||
"Ice": 0x11,
|
||||
"Fire": 0x12,
|
||||
"Water": 0x13,
|
||||
"Ghost": 0x14,
|
||||
"Ground": 0x15,
|
||||
"Grass": 0x16,
|
||||
"Psychic": 0x17,
|
||||
"Electric": 0x18,
|
||||
"Rock": 0x19,
|
||||
"Dragon": 0x1F,
|
||||
"Flying": 0x20,
|
||||
"Fighting": 0x21,
|
||||
"Bug": 0x22
|
||||
}
|
||||
palettes = []
|
||||
for mon in poke_data.pokemon_data:
|
||||
if self.multiworld.randomize_pokemon_palettes[self.player] == "primary_type":
|
||||
pallet = pallet_map[self.local_poke_data[mon]["type1"]]
|
||||
elif (self.multiworld.randomize_pokemon_palettes[self.player] == "follow_evolutions" and mon in
|
||||
poke_data.evolves_from and poke_data.evolves_from[mon] != "Eevee"):
|
||||
pallet = palettes[-1]
|
||||
else: # completely_random or follow_evolutions and it is not an evolved form (except eeveelutions)
|
||||
pallet = random.choice(list(pallet_map.values()))
|
||||
palettes.append(pallet)
|
||||
address = rom_addresses["Mon_Palettes"]
|
||||
for pallet in palettes:
|
||||
data[address] = pallet
|
||||
address += 1
|
||||
|
||||
|
||||
def process_trainer_data(self, data, random):
|
||||
mons_list = [pokemon for pokemon in poke_data.pokemon_data.keys() if pokemon not in poke_data.legendary_pokemon
|
||||
or self.multiworld.trainer_legendaries[self.player].value]
|
||||
|
@ -163,6 +200,7 @@ def process_static_pokemon(self):
|
|||
randomize_type, self.multiworld.random))
|
||||
location.place_locked_item(mon)
|
||||
|
||||
chosen_mons = set()
|
||||
for slot in starter_slots:
|
||||
location = self.multiworld.get_location(slot.name, self.player)
|
||||
randomize_type = self.multiworld.randomize_starter_pokemon[self.player].value
|
||||
|
@ -170,9 +208,13 @@ def process_static_pokemon(self):
|
|||
if not randomize_type:
|
||||
location.place_locked_item(self.create_item(slot_type + " " + slot.original_item))
|
||||
else:
|
||||
location.place_locked_item(self.create_item(slot_type + " " +
|
||||
randomize_pokemon(self, slot.original_item, mons_list, randomize_type,
|
||||
self.multiworld.random)))
|
||||
mon = self.create_item(slot_type + " " + randomize_pokemon(self, slot.original_item, mons_list,
|
||||
randomize_type, self.multiworld.random))
|
||||
while mon.name in chosen_mons:
|
||||
mon = self.create_item(slot_type + " " + randomize_pokemon(self, slot.original_item, mons_list,
|
||||
randomize_type, self.multiworld.random))
|
||||
chosen_mons.add(mon.name)
|
||||
location.place_locked_item(mon)
|
||||
|
||||
|
||||
def process_wild_pokemon(self):
|
||||
|
@ -180,27 +222,36 @@ def process_wild_pokemon(self):
|
|||
encounter_slots = get_encounter_slots(self)
|
||||
|
||||
placed_mons = {pokemon: 0 for pokemon in poke_data.pokemon_data.keys()}
|
||||
zone_mapping = {}
|
||||
if self.multiworld.randomize_wild_pokemon[self.player].value:
|
||||
mons_list = [pokemon for pokemon in poke_data.pokemon_data.keys() if pokemon not in poke_data.legendary_pokemon
|
||||
or self.multiworld.randomize_legendary_pokemon[self.player].value == 3]
|
||||
self.multiworld.random.shuffle(encounter_slots)
|
||||
locations = []
|
||||
for slot in encounter_slots:
|
||||
mon = randomize_pokemon(self, slot.original_item, mons_list,
|
||||
self.multiworld.randomize_wild_pokemon[self.player].value, self.multiworld.random)
|
||||
location = self.multiworld.get_location(slot.name, self.player)
|
||||
zone = " - ".join(location.name.split(" - ")[:-1])
|
||||
if zone not in zone_mapping:
|
||||
zone_mapping[zone] = {}
|
||||
original_mon = slot.original_item
|
||||
if self.multiworld.area_1_to_1_mapping[self.player] and original_mon in zone_mapping[zone]:
|
||||
mon = zone_mapping[zone][original_mon]
|
||||
else:
|
||||
mon = randomize_pokemon(self, original_mon, mons_list,
|
||||
self.multiworld.randomize_wild_pokemon[self.player].value, self.multiworld.random)
|
||||
# if static Pokemon are not randomized, we make sure nothing on Pokemon Tower 6F is a Marowak
|
||||
# if static Pokemon are randomized we deal with that during static encounter randomization
|
||||
while (self.multiworld.randomize_static_pokemon[self.player].value == 0 and mon == "Marowak"
|
||||
and "Pokemon Tower 6F" in slot.name):
|
||||
# to account for the possibility that only one ground type Pokemon exists, match only stats for this fix
|
||||
mon = randomize_pokemon(self, slot.original_item, mons_list, 2, self.multiworld.random)
|
||||
mon = randomize_pokemon(self, original_mon, mons_list, 2, self.multiworld.random)
|
||||
placed_mons[mon] += 1
|
||||
location = self.multiworld.get_location(slot.name, self.player)
|
||||
location.item = self.create_item(mon)
|
||||
location.event = True
|
||||
location.locked = True
|
||||
location.item.location = location
|
||||
locations.append(location)
|
||||
zone_mapping[zone][original_mon] = mon
|
||||
|
||||
mons_to_add = []
|
||||
remaining_pokemon = [pokemon for pokemon in poke_data.pokemon_data.keys() if placed_mons[pokemon] == 0 and
|
||||
|
@ -223,22 +274,46 @@ def process_wild_pokemon(self):
|
|||
for mon in mons_to_add:
|
||||
stat_base = get_base_stat_total(mon)
|
||||
candidate_locations = get_encounter_slots(self)
|
||||
if self.multiworld.randomize_wild_pokemon[self.player].value in [1, 3]:
|
||||
candidate_locations = [slot for slot in candidate_locations if any([poke_data.pokemon_data[slot.original_item][
|
||||
"type1"] in [self.local_poke_data[mon]["type1"], self.local_poke_data[mon]["type2"]],
|
||||
poke_data.pokemon_data[slot.original_item]["type2"] in [self.local_poke_data[mon]["type1"],
|
||||
self.local_poke_data[mon]["type2"]]])]
|
||||
if not candidate_locations:
|
||||
candidate_locations = get_encounter_slots(self)
|
||||
candidate_locations = [self.multiworld.get_location(location.name, self.player) for location in candidate_locations]
|
||||
candidate_locations.sort(key=lambda slot: abs(get_base_stat_total(slot.item.name) - stat_base))
|
||||
if self.multiworld.randomize_wild_pokemon[self.player].current_key in ["match_base_stats", "match_types_and_base_stats"]:
|
||||
candidate_locations.sort(key=lambda slot: abs(get_base_stat_total(slot.item.name) - stat_base))
|
||||
if self.multiworld.randomize_wild_pokemon[self.player].current_key in ["match_types", "match_types_and_base_stats"]:
|
||||
candidate_locations.sort(key=lambda slot: not any([poke_data.pokemon_data[slot.original_item]["type1"] in
|
||||
[self.local_poke_data[mon]["type1"], self.local_poke_data[mon]["type2"]],
|
||||
poke_data.pokemon_data[slot.original_item]["type2"] in
|
||||
[self.local_poke_data[mon]["type1"], self.local_poke_data[mon]["type2"]]]))
|
||||
for location in candidate_locations:
|
||||
if placed_mons[location.item.name] > 1 or location.item.name not in poke_data.first_stage_pokemon:
|
||||
placed_mons[location.item.name] -= 1
|
||||
location.item = self.create_item(mon)
|
||||
location.item.location = location
|
||||
zone = " - ".join(location.name.split(" - ")[:-1])
|
||||
if self.multiworld.catch_em_all[self.player] == "all_pokemon" and self.multiworld.area_1_to_1_mapping[self.player]:
|
||||
if not [self.multiworld.get_location(l.name, self.player) for l in get_encounter_slots(self)
|
||||
if (not l.name.startswith(zone)) and
|
||||
self.multiworld.get_location(l.name, self.player).item.name == location.item.name]:
|
||||
continue
|
||||
if self.multiworld.catch_em_all[self.player] == "first_stage" and self.multiworld.area_1_to_1_mapping[self.player]:
|
||||
if not [self.multiworld.get_location(l.name, self.player) for l in get_encounter_slots(self)
|
||||
if (not l.name.startswith(zone)) and
|
||||
self.multiworld.get_location(l.name, self.player).item.name == location.item.name and l.name
|
||||
not in poke_data.evolves_from]:
|
||||
continue
|
||||
|
||||
if placed_mons[location.item.name] < 2 and (location.item.name in poke_data.first_stage_pokemon
|
||||
or self.multiworld.catch_em_all[self.player]):
|
||||
continue
|
||||
|
||||
if self.multiworld.area_1_to_1_mapping[self.player]:
|
||||
place_locations = [place_location for place_location in candidate_locations if
|
||||
place_location.name.startswith(zone) and
|
||||
place_location.item.name == location.item.name]
|
||||
else:
|
||||
place_locations = [location]
|
||||
for place_location in place_locations:
|
||||
placed_mons[place_location.item.name] -= 1
|
||||
place_location.item = self.create_item(mon)
|
||||
place_location.item.location = place_location
|
||||
placed_mons[mon] += 1
|
||||
break
|
||||
break
|
||||
else:
|
||||
raise Exception
|
||||
|
||||
else:
|
||||
for slot in encounter_slots:
|
||||
|
@ -250,10 +325,41 @@ def process_wild_pokemon(self):
|
|||
placed_mons[location.item.name] += 1
|
||||
|
||||
|
||||
def process_move_data(self):
|
||||
self.local_move_data = deepcopy(poke_data.moves)
|
||||
if self.multiworld.move_balancing[self.player]:
|
||||
self.local_move_data["Sing"]["accuracy"] = 30
|
||||
self.local_move_data["Sleep Powder"]["accuracy"] = 40
|
||||
self.local_move_data["Spore"]["accuracy"] = 50
|
||||
self.local_move_data["Sonicboom"]["effect"] = 0
|
||||
self.local_move_data["Sonicboom"]["power"] = 50
|
||||
self.local_move_data["Dragon Rage"]["effect"] = 0
|
||||
self.local_move_data["Dragon Rage"]["power"] = 80
|
||||
self.local_move_data["Horn Drill"]["effect"] = 0
|
||||
self.local_move_data["Horn Drill"]["power"] = 70
|
||||
self.local_move_data["Horn Drill"]["accuracy"] = 90
|
||||
self.local_move_data["Guillotine"]["effect"] = 0
|
||||
self.local_move_data["Guillotine"]["power"] = 70
|
||||
self.local_move_data["Guillotine"]["accuracy"] = 90
|
||||
self.local_move_data["Fissure"]["effect"] = 0
|
||||
self.local_move_data["Fissure"]["power"] = 70
|
||||
self.local_move_data["Fissure"]["accuracy"] = 90
|
||||
self.local_move_data["Blizzard"]["accuracy"] = 70
|
||||
if self.multiworld.randomize_tm_moves[self.player]:
|
||||
self.local_tms = self.multiworld.random.sample([move for move in poke_data.moves.keys() if move not in
|
||||
["No Move"] + poke_data.hm_moves], 50)
|
||||
else:
|
||||
self.local_tms = poke_data.tm_moves.copy()
|
||||
|
||||
|
||||
def process_pokemon_data(self):
|
||||
|
||||
local_poke_data = deepcopy(poke_data.pokemon_data)
|
||||
learnsets = deepcopy(poke_data.learnsets)
|
||||
tms_hms = self.local_tms + poke_data.hm_moves
|
||||
|
||||
|
||||
compat_hms = set()
|
||||
|
||||
for mon, mon_data in local_poke_data.items():
|
||||
if self.multiworld.randomize_pokemon_stats[self.player].value == 1:
|
||||
|
@ -265,18 +371,21 @@ def process_pokemon_data(self):
|
|||
mon_data["spd"] = stats[3]
|
||||
mon_data["spc"] = stats[4]
|
||||
elif self.multiworld.randomize_pokemon_stats[self.player].value == 2:
|
||||
old_stats = mon_data["hp"] + mon_data["atk"] + mon_data["def"] + mon_data["spd"] + mon_data["spc"] - 5
|
||||
stats = [1, 1, 1, 1, 1]
|
||||
while old_stats > 0:
|
||||
stat = self.multiworld.random.randint(0, 4)
|
||||
if stats[stat] < 255:
|
||||
old_stats -= 1
|
||||
stats[stat] += 1
|
||||
mon_data["hp"] = stats[0]
|
||||
mon_data["atk"] = stats[1]
|
||||
mon_data["def"] = stats[2]
|
||||
mon_data["spd"] = stats[3]
|
||||
mon_data["spc"] = stats[4]
|
||||
first_run = True
|
||||
while (mon_data["hp"] > 255 or mon_data["atk"] > 255 or mon_data["def"] > 255 or mon_data["spd"] > 255
|
||||
or mon_data["spc"] > 255 or first_run):
|
||||
first_run = False
|
||||
total_stats = mon_data["hp"] + mon_data["atk"] + mon_data["def"] + mon_data["spd"] + mon_data["spc"] - 60
|
||||
dist = [self.multiworld.random.randint(1, 101) / 100, self.multiworld.random.randint(1, 101) / 100,
|
||||
self.multiworld.random.randint(1, 101) / 100, self.multiworld.random.randint(1, 101) / 100,
|
||||
self.multiworld.random.randint(1, 101) / 100]
|
||||
total_dist = sum(dist)
|
||||
|
||||
mon_data["hp"] = int(round(dist[0] / total_dist * total_stats) + 20)
|
||||
mon_data["atk"] = int(round(dist[1] / total_dist * total_stats) + 10)
|
||||
mon_data["def"] = int(round(dist[2] / total_dist * total_stats) + 10)
|
||||
mon_data["spd"] = int(round(dist[3] / total_dist * total_stats) + 10)
|
||||
mon_data["spc"] = int(round(dist[4] / total_dist * total_stats) + 10)
|
||||
if self.multiworld.randomize_pokemon_types[self.player].value:
|
||||
if self.multiworld.randomize_pokemon_types[self.player].value == 1 and mon in poke_data.evolves_from:
|
||||
type1 = local_poke_data[poke_data.evolves_from[mon]]["type1"]
|
||||
|
@ -318,46 +427,237 @@ def process_pokemon_data(self):
|
|||
moves = list(poke_data.moves.keys())
|
||||
for move in ["No Move"] + poke_data.hm_moves:
|
||||
moves.remove(move)
|
||||
mon_data["start move 1"] = get_move(moves, chances, self.multiworld.random, True)
|
||||
for i in range(2, 5):
|
||||
if mon_data[f"start move {i}"] != "No Move" or self.multiworld.start_with_four_moves[
|
||||
self.player].value == 1:
|
||||
mon_data[f"start move {i}"] = get_move(moves, chances, self.multiworld.random)
|
||||
if self.multiworld.confine_transform_to_ditto[self.player]:
|
||||
moves.remove("Transform")
|
||||
if self.multiworld.start_with_four_moves[self.player]:
|
||||
num_moves = 4
|
||||
else:
|
||||
num_moves = len([i for i in [mon_data["start move 1"], mon_data["start move 2"],
|
||||
mon_data["start move 3"], mon_data["start move 4"]] if i != "No Move"])
|
||||
if mon in learnsets:
|
||||
for move_num in range(0, len(learnsets[mon])):
|
||||
learnsets[mon][move_num] = get_move(moves, chances, self.multiworld.random)
|
||||
num_moves += len(learnsets[mon])
|
||||
non_power_moves = []
|
||||
learnsets[mon] = []
|
||||
for i in range(num_moves):
|
||||
if i == 0 and mon == "Ditto" and self.multiworld.confine_transform_to_ditto[self.player]:
|
||||
move = "Transform"
|
||||
else:
|
||||
move = get_move(self.local_move_data, moves, chances, self.multiworld.random)
|
||||
while move == "Transform" and self.multiworld.confine_transform_to_ditto[self.player]:
|
||||
move = get_move(self.local_move_data, moves, chances, self.multiworld.random)
|
||||
if self.local_move_data[move]["power"] < 5:
|
||||
non_power_moves.append(move)
|
||||
else:
|
||||
learnsets[mon].append(move)
|
||||
learnsets[mon].sort(key=lambda move: self.local_move_data[move]["power"])
|
||||
if learnsets[mon]:
|
||||
for move in non_power_moves:
|
||||
learnsets[mon].insert(self.multiworld.random.randint(1, len(learnsets[mon])), move)
|
||||
else:
|
||||
learnsets[mon] = non_power_moves
|
||||
for i in range(1, 5):
|
||||
if mon_data[f"start move {i}"] != "No Move" or self.multiworld.start_with_four_moves[self.player]:
|
||||
mon_data[f"start move {i}"] = learnsets[mon].pop(0)
|
||||
|
||||
if self.multiworld.randomize_pokemon_catch_rates[self.player].value:
|
||||
mon_data["catch rate"] = self.multiworld.random.randint(self.multiworld.minimum_catch_rate[self.player], 255)
|
||||
else:
|
||||
mon_data["catch rate"] = max(self.multiworld.minimum_catch_rate[self.player], mon_data["catch rate"])
|
||||
|
||||
if mon != "Mew":
|
||||
tms_hms = poke_data.tm_moves + poke_data.hm_moves
|
||||
for flag, tm_move in enumerate(tms_hms):
|
||||
if ((mon in poke_data.evolves_from.keys() and mon_data["type1"] ==
|
||||
local_poke_data[poke_data.evolves_from[mon]]["type1"] and mon_data["type2"] ==
|
||||
local_poke_data[poke_data.evolves_from[mon]]["type2"]) and (
|
||||
(flag < 50 and self.multiworld.tm_compatibility[self.player].value in [1, 2]) or (
|
||||
flag >= 51 and self.multiworld.hm_compatibility[self.player].value in [1, 2]))):
|
||||
bit = 1 if local_poke_data[poke_data.evolves_from[mon]]["tms"][int(flag / 8)] & 1 << (flag % 8) else 0
|
||||
elif (flag < 50 and self.multiworld.tm_compatibility[self.player].value == 1) or (flag >= 50 and self.multiworld.hm_compatibility[self.player].value == 1):
|
||||
type_match = poke_data.moves[tm_move]["type"] in [mon_data["type1"], mon_data["type2"]]
|
||||
bit = int(self.multiworld.random.randint(1, 100) < [[90, 50, 25], [100, 75, 25]][flag >= 50][0 if type_match else 1 if poke_data.moves[tm_move]["type"] == "Normal" else 2])
|
||||
elif (flag < 50 and self.multiworld.tm_compatibility[self.player].value == 2) or (flag >= 50 and self.multiworld.hm_compatibility[self.player].value == 2):
|
||||
bit = self.multiworld.random.randint(0, 1)
|
||||
elif (flag < 50 and self.multiworld.tm_compatibility[self.player].value == 3) or (flag >= 50 and self.multiworld.hm_compatibility[self.player].value == 3):
|
||||
def roll_tm_compat(roll_move):
|
||||
if self.local_move_data[roll_move]["type"] in [mon_data["type1"], mon_data["type2"]]:
|
||||
if roll_move in poke_data.hm_moves:
|
||||
if self.multiworld.hm_same_type_compatibility[self.player].value == -1:
|
||||
return mon_data["tms"][int(flag / 8)]
|
||||
r = self.multiworld.random.randint(1, 100) <= self.multiworld.hm_same_type_compatibility[self.player].value
|
||||
if r and mon not in poke_data.legendary_pokemon:
|
||||
compat_hms.add(roll_move)
|
||||
return r
|
||||
else:
|
||||
if self.multiworld.tm_same_type_compatibility[self.player].value == -1:
|
||||
return mon_data["tms"][int(flag / 8)]
|
||||
return self.multiworld.random.randint(1, 100) <= self.multiworld.tm_same_type_compatibility[self.player].value
|
||||
elif self.local_move_data[roll_move]["type"] == "Normal" and "Normal" not in [mon_data["type1"], mon_data["type2"]]:
|
||||
if roll_move in poke_data.hm_moves:
|
||||
if self.multiworld.hm_normal_type_compatibility[self.player].value == -1:
|
||||
return mon_data["tms"][int(flag / 8)]
|
||||
r = self.multiworld.random.randint(1, 100) <= self.multiworld.hm_normal_type_compatibility[self.player].value
|
||||
if r and mon not in poke_data.legendary_pokemon:
|
||||
compat_hms.add(roll_move)
|
||||
return r
|
||||
else:
|
||||
if self.multiworld.tm_normal_type_compatibility[self.player].value == -1:
|
||||
return mon_data["tms"][int(flag / 8)]
|
||||
return self.multiworld.random.randint(1, 100) <= self.multiworld.tm_normal_type_compatibility[self.player].value
|
||||
else:
|
||||
if roll_move in poke_data.hm_moves:
|
||||
if self.multiworld.hm_other_type_compatibility[self.player].value == -1:
|
||||
return mon_data["tms"][int(flag / 8)]
|
||||
r = self.multiworld.random.randint(1, 100) <= self.multiworld.hm_other_type_compatibility[self.player].value
|
||||
if r and mon not in poke_data.legendary_pokemon:
|
||||
compat_hms.add(roll_move)
|
||||
return r
|
||||
else:
|
||||
if self.multiworld.tm_other_type_compatibility[self.player].value == -1:
|
||||
return mon_data["tms"][int(flag / 8)]
|
||||
return self.multiworld.random.randint(1, 100) <= self.multiworld.tm_other_type_compatibility[self.player].value
|
||||
|
||||
|
||||
for flag, tm_move in enumerate(tms_hms):
|
||||
if mon in poke_data.evolves_from.keys() and self.multiworld.inherit_tm_hm_compatibility[self.player]:
|
||||
|
||||
if local_poke_data[poke_data.evolves_from[mon]]["tms"][int(flag / 8)] & 1 << (flag % 8):
|
||||
# always inherit learnable tms/hms
|
||||
bit = 1
|
||||
else:
|
||||
continue
|
||||
if bit:
|
||||
mon_data["tms"][int(flag / 8)] |= 1 << (flag % 8)
|
||||
else:
|
||||
mon_data["tms"][int(flag / 8)] &= ~(1 << (flag % 8))
|
||||
if self.local_move_data[tm_move]["type"] in [mon_data["type1"], mon_data["type2"]] and \
|
||||
self.local_move_data[tm_move]["type"] not in [
|
||||
local_poke_data[poke_data.evolves_from[mon]]["type1"],
|
||||
local_poke_data[poke_data.evolves_from[mon]]["type2"]]:
|
||||
# the tm/hm is for a move whose type matches current mon, but not pre-evolved form
|
||||
# so this gets full chance roll
|
||||
bit = roll_tm_compat(tm_move)
|
||||
# otherwise 50% reduced chance to add compatibility over pre-evolved form
|
||||
elif self.multiworld.random.randint(1, 100) > 50 and roll_tm_compat(tm_move):
|
||||
bit = 1
|
||||
else:
|
||||
bit = 0
|
||||
else:
|
||||
bit = roll_tm_compat(tm_move)
|
||||
if bit:
|
||||
mon_data["tms"][int(flag / 8)] |= 1 << (flag % 8)
|
||||
else:
|
||||
mon_data["tms"][int(flag / 8)] &= ~(1 << (flag % 8))
|
||||
|
||||
hm_verify = ["Surf", "Strength"]
|
||||
if self.multiworld.accessibility[self.player] != "minimal" or ((not
|
||||
self.multiworld.badgesanity[self.player]) and max(self.multiworld.elite_four_condition[self.player],
|
||||
self.multiworld.victory_road_condition[self.player]) > 7):
|
||||
hm_verify += ["Cut"]
|
||||
if self.multiworld.accessibility[self.player] != "minimal" and (self.multiworld.trainersanity[self.player] or
|
||||
self.multiworld.extra_key_items[self.player]):
|
||||
hm_verify += ["Flash"]
|
||||
|
||||
for hm_move in hm_verify:
|
||||
if hm_move not in compat_hms:
|
||||
mon = self.multiworld.random.choice([mon for mon in poke_data.pokemon_data if mon not in
|
||||
poke_data.legendary_pokemon])
|
||||
flag = tms_hms.index(hm_move)
|
||||
local_poke_data[mon]["tms"][int(flag / 8)] |= 1 << (flag % 8)
|
||||
|
||||
self.local_poke_data = local_poke_data
|
||||
self.learnsets = learnsets
|
||||
|
||||
|
||||
def write_quizzes(self, data, random):
|
||||
|
||||
def get_quiz(q, a):
|
||||
if q == 0:
|
||||
r = random.randint(0, 3)
|
||||
if r == 0:
|
||||
mon = self.trade_mons["Trade_Dux"]
|
||||
text = "A woman in<LINE>Vermilion City<CONT>"
|
||||
elif r == 1:
|
||||
mon = self.trade_mons["Trade_Lola"]
|
||||
text = "A man in<LINE>Cerulean City<CONT>"
|
||||
elif r == 2:
|
||||
mon = self.trade_mons["Trade_Marcel"]
|
||||
text = "Someone on Route 2<LINE>"
|
||||
elif r == 3:
|
||||
mon = self.trade_mons["Trade_Spot"]
|
||||
text = "Someone on Route 5<LINE>"
|
||||
if not a:
|
||||
answers.append(0)
|
||||
old_mon = mon
|
||||
while old_mon == mon:
|
||||
mon = random.choice(list(poke_data.pokemon_data.keys()))
|
||||
|
||||
return encode_text(f"{text}was looking for<CONT>{mon}?<DONE>")
|
||||
elif q == 1:
|
||||
for location in self.multiworld.get_filled_locations():
|
||||
if location.item.name == "Secret Key" and location.item.player == self.player:
|
||||
break
|
||||
if location.player == self.player:
|
||||
player_name = "yourself"
|
||||
else:
|
||||
player_name = self.multiworld.player_names[location.player]
|
||||
if not a:
|
||||
if len(self.multiworld.player_name) > 1:
|
||||
old_name = player_name
|
||||
while old_name == player_name:
|
||||
player_name = random.choice(list(self.multiworld.player_name.values()))
|
||||
else:
|
||||
return encode_text("You're playing<LINE>in a multiworld<CONT>with other<CONT>players?<DONE>")
|
||||
return encode_text(f"The Secret Key was<LINE>found by<CONT>{player_name[:17]}?<DONE>")
|
||||
elif q == 2:
|
||||
if a:
|
||||
return encode_text(f"#mon is<LINE>pronounced<CONT>Po-kay-mon?<DONE>")
|
||||
else:
|
||||
if random.randint(0, 1):
|
||||
return encode_text(f"#mon is<LINE>pronounced<CONT>Po-key-mon?<DONE>")
|
||||
else:
|
||||
return encode_text(f"#mon is<LINE>pronounced<CONT>Po-kuh-mon?<DONE>")
|
||||
elif q == 3:
|
||||
starters = [" ".join(self.multiworld.get_location(
|
||||
f"Pallet Town - Starter {i}", self.player).item.name.split(" ")[1:]) for i in range(1, 4)]
|
||||
mon = random.choice(starters)
|
||||
nots = random.choice(range(8, 16, 2))
|
||||
if random.randint(0, 1):
|
||||
while mon in starters:
|
||||
mon = random.choice(list(poke_data.pokemon_data.keys()))
|
||||
if a:
|
||||
nots += 1
|
||||
elif not a:
|
||||
nots += 1
|
||||
text = f"{mon} was<LINE>"
|
||||
while nots > 0:
|
||||
i = random.randint(1, min(4, nots))
|
||||
text += ("not " * i) + "<CONT>"
|
||||
nots -= i
|
||||
text += "a starter choice?<DONE>"
|
||||
return encode_text(text)
|
||||
elif q == 4:
|
||||
if a:
|
||||
tm_text = self.local_tms[27]
|
||||
else:
|
||||
if self.multiworld.randomize_tm_moves[self.player]:
|
||||
wrong_tms = self.local_tms.copy()
|
||||
wrong_tms.pop(27)
|
||||
tm_text = random.choice(wrong_tms)
|
||||
else:
|
||||
tm_text = "TOMBSTONER"
|
||||
return encode_text(f"TM28 contains<LINE>{tm_text.upper()}?<DONE>")
|
||||
elif q == 5:
|
||||
i = 8
|
||||
while not a and i in [1, 8]:
|
||||
i = random.randint(0, 99999999)
|
||||
return encode_text(f"There are {i}<LINE>certified #MON<CONT>LEAGUE BADGEs?<DONE>")
|
||||
elif q == 6:
|
||||
i = 2
|
||||
while not a and i in [1, 2]:
|
||||
i = random.randint(0, 99)
|
||||
return encode_text(f"POLIWAG evolves {i}<LINE>times?<DONE>")
|
||||
elif q == 7:
|
||||
entity = "Motor Carrier"
|
||||
if not a:
|
||||
entity = random.choice(["Driver", "Shipper"])
|
||||
return encode_text("Title 49 of the<LINE>U.S. Code of<CONT>Federal<CONT>Regulations part<CONT>397.67 states"
|
||||
f"<CONT>that the<CONT>{entity}<CONT>is responsible<CONT>for planning<CONT>routes when"
|
||||
"<CONT>hazardous<CONT>materials are<CONT>transported?<DONE>")
|
||||
|
||||
answers = [random.randint(0, 1), random.randint(0, 1), random.randint(0, 1),
|
||||
random.randint(0, 1), random.randint(0, 1), random.randint(0, 1)]
|
||||
|
||||
questions = random.sample((range(0, 8)), 6)
|
||||
question_texts = []
|
||||
for i, question in enumerate(questions):
|
||||
question_texts.append(get_quiz(question, answers[i]))
|
||||
|
||||
for i, quiz in enumerate(["A", "B", "C", "D", "E", "F"]):
|
||||
data[rom_addresses[f"Quiz_Answer_{quiz}"]] = int(not answers[i]) << 4 | (i + 1)
|
||||
write_bytes(data, question_texts[i], rom_addresses[f"Text_Quiz_{quiz}"])
|
||||
|
||||
|
||||
def generate_output(self, output_directory: str):
|
||||
random = self.multiworld.per_slot_randoms[self.player]
|
||||
game_version = self.multiworld.game_version[self.player].current_key
|
||||
|
@ -384,10 +684,33 @@ def generate_output(self, output_directory: str):
|
|||
elif " ".join(location.item.name.split()[1:]) in poke_data.pokemon_data.keys():
|
||||
data[address] = poke_data.pokemon_data[" ".join(location.item.name.split()[1:])]["id"]
|
||||
else:
|
||||
data[address] = self.item_name_to_id[location.item.name] - 172000000
|
||||
item_id = self.item_name_to_id[location.item.name] - 172000000
|
||||
if item_id > 255:
|
||||
item_id -= 256
|
||||
data[address] = item_id
|
||||
|
||||
else:
|
||||
data[location.rom_address] = 0x2C # AP Item
|
||||
|
||||
def set_trade_mon(address, loc):
|
||||
mon = self.multiworld.get_location(loc, self.player).item.name
|
||||
data[rom_addresses[address]] = poke_data.pokemon_data[mon]["id"]
|
||||
self.trade_mons[address] = mon
|
||||
|
||||
if game_version == "red":
|
||||
set_trade_mon("Trade_Terry", "Safari Zone Center - Wild Pokemon - 5")
|
||||
set_trade_mon("Trade_Spot", "Safari Zone East - Wild Pokemon - 1")
|
||||
else:
|
||||
set_trade_mon("Trade_Terry", "Safari Zone Center - Wild Pokemon - 7")
|
||||
set_trade_mon("Trade_Spot", "Safari Zone East - Wild Pokemon - 7")
|
||||
set_trade_mon("Trade_Marcel", "Route 24 - Wild Pokemon - 6")
|
||||
set_trade_mon("Trade_Sailor", "Pokemon Mansion 1F - Wild Pokemon - 3")
|
||||
set_trade_mon("Trade_Dux", "Route 3 - Wild Pokemon - 2")
|
||||
set_trade_mon("Trade_Marc", "Route 23 - Super Rod Pokemon - 1")
|
||||
set_trade_mon("Trade_Lola", "Route 10 - Super Rod Pokemon - 1")
|
||||
set_trade_mon("Trade_Doris", "Cerulean Cave 1F - Wild Pokemon - 9")
|
||||
set_trade_mon("Trade_Crinkles", "Route 12 - Wild Pokemon - 4")
|
||||
|
||||
data[rom_addresses['Fly_Location']] = self.fly_map_code
|
||||
|
||||
if self.multiworld.tea[self.player].value:
|
||||
|
@ -421,6 +744,14 @@ def generate_output(self, output_directory: str):
|
|||
if self.multiworld.old_man[self.player].value == 2:
|
||||
data[rom_addresses['Option_Old_Man']] = 0x11
|
||||
data[rom_addresses['Option_Old_Man_Lying']] = 0x15
|
||||
if self.multiworld.require_pokedex[self.player]:
|
||||
data[rom_addresses["Require_Pokedex_A"]] = 1
|
||||
data[rom_addresses["Require_Pokedex_B"]] = 1
|
||||
if self.multiworld.dexsanity[self.player]:
|
||||
data[rom_addresses["Option_Dexsanity_A"]] = 1
|
||||
data[rom_addresses["Option_Dexsanity_B"]] = 1
|
||||
if self.multiworld.all_pokemon_seen[self.player]:
|
||||
data[rom_addresses["Option_Pokedex_Seen"]] = 1
|
||||
money = str(self.multiworld.starting_money[self.player].value).zfill(6)
|
||||
data[rom_addresses["Starting_Money_High"]] = int(money[:2], 16)
|
||||
data[rom_addresses["Starting_Money_Middle"]] = int(money[2:4], 16)
|
||||
|
@ -433,6 +764,7 @@ def generate_output(self, output_directory: str):
|
|||
write_bytes(data, encode_text(
|
||||
" ".join(self.multiworld.get_location("Route 3 - Pokemon For Sale", self.player).item.name.upper().split()[1:])),
|
||||
rom_addresses["Text_Magikarp_Salesman"])
|
||||
write_quizzes(self, data, random)
|
||||
|
||||
if self.multiworld.badges_needed_for_hm_moves[self.player].value == 0:
|
||||
for hm_move in poke_data.hm_moves:
|
||||
|
@ -492,10 +824,10 @@ def generate_output(self, output_directory: str):
|
|||
data[address + 17] = poke_data.moves[self.local_poke_data[mon]["start move 3"]]["id"]
|
||||
data[address + 18] = poke_data.moves[self.local_poke_data[mon]["start move 4"]]["id"]
|
||||
write_bytes(data, self.local_poke_data[mon]["tms"], address + 20)
|
||||
if mon in self.learnsets:
|
||||
address = rom_addresses["Learnset_" + mon.replace(" ", "")]
|
||||
for i, move in enumerate(self.learnsets[mon]):
|
||||
data[(address + 1) + i * 2] = poke_data.moves[move]["id"]
|
||||
if mon in self.learnsets and self.learnsets[mon]:
|
||||
address = rom_addresses["Learnset_" + mon.replace(" ", "")]
|
||||
for i, move in enumerate(self.learnsets[mon]):
|
||||
data[(address + 1) + i * 2] = poke_data.moves[move]["id"]
|
||||
|
||||
data[rom_addresses["Option_Aide_Rt2"]] = self.multiworld.oaks_aide_rt_2[self.player].value
|
||||
data[rom_addresses["Option_Aide_Rt11"]] = self.multiworld.oaks_aide_rt_11[self.player].value
|
||||
|
@ -507,8 +839,8 @@ def generate_output(self, output_directory: str):
|
|||
if self.multiworld.reusable_tms[self.player].value:
|
||||
data[rom_addresses["Option_Reusable_TMs"]] = 0xC9
|
||||
|
||||
data[rom_addresses["Option_Trainersanity"]] = self.multiworld.trainersanity[self.player].value
|
||||
data[rom_addresses["Option_Trainersanity2"]] = self.multiworld.trainersanity[self.player].value
|
||||
for i in range(1, 10):
|
||||
data[rom_addresses[f"Option_Trainersanity{i}"]] = self.multiworld.trainersanity[self.player].value
|
||||
|
||||
data[rom_addresses["Option_Always_Half_STAB"]] = int(not self.multiworld.same_type_attack_bonus[self.player].value)
|
||||
|
||||
|
@ -532,8 +864,23 @@ def generate_output(self, output_directory: str):
|
|||
if data[rom_addresses["Start_Inventory"] + item.code - 172000000] < 255:
|
||||
data[rom_addresses["Start_Inventory"] + item.code - 172000000] += 1
|
||||
|
||||
set_mon_palettes(self, random, data)
|
||||
process_trainer_data(self, data, random)
|
||||
|
||||
for move_data in self.local_move_data.values():
|
||||
if move_data["id"] == 0:
|
||||
continue
|
||||
address = rom_addresses["Move_Data"] + ((move_data["id"] - 1) * 6)
|
||||
write_bytes(data, bytearray([move_data["id"], move_data["effect"], move_data["power"],
|
||||
poke_data.type_ids[move_data["type"]], round(move_data["accuracy"] * 2.55), move_data["pp"]]), address)
|
||||
|
||||
TM_IDs = bytearray([poke_data.moves[move]["id"] for move in self.local_tms])
|
||||
write_bytes(data, TM_IDs, rom_addresses["TM_Moves"])
|
||||
|
||||
if self.multiworld.randomize_rock_tunnel[self.player]:
|
||||
seed = randomize_rock_tunnel(data, random)
|
||||
write_bytes(data, encode_text(f"SEED: <LINE>{seed}"), rom_addresses["Text_Rock_Tunnel_Sign"])
|
||||
|
||||
mons = [mon["id"] for mon in poke_data.pokemon_data.values()]
|
||||
random.shuffle(mons)
|
||||
data[rom_addresses['Title_Mon_First']] = mons.pop()
|
||||
|
@ -564,7 +911,7 @@ def generate_output(self, output_directory: str):
|
|||
else:
|
||||
write_bytes(data, self.rival_name, rom_addresses['Rival_Name'])
|
||||
|
||||
data[0xFF00] = 1 # client compatibility version
|
||||
data[0xFF00] = 2 # client compatibility version
|
||||
write_bytes(data, self.multiworld.seed_name.encode(), 0xFFDB)
|
||||
write_bytes(data, self.multiworld.player_name[self.player].encode(), 0xFFF0)
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,26 +1,45 @@
|
|||
from ..generic.Rules import add_item_rule, add_rule
|
||||
from ..generic.Rules import add_item_rule, add_rule, item_name
|
||||
from .items import item_groups
|
||||
|
||||
|
||||
def set_rules(world, player):
|
||||
|
||||
add_item_rule(world.get_location("Pallet Town - Player's PC", player),
|
||||
lambda i: i.player == player and "Badge" not in i.name and "Trap" not in i.name and
|
||||
i.name != "Pokedex")
|
||||
item_rules = {
|
||||
"Pallet Town - Player's PC": (lambda i: i.player == player and "Badge" not in i.name and "Trap" not in i.name
|
||||
and i.name != "Pokedex" and "Coins" not in i.name)
|
||||
}
|
||||
|
||||
if world.prizesanity[player]:
|
||||
def prize_rule(i):
|
||||
return i.player != player or i.name in item_groups["Unique"]
|
||||
item_rules["Celadon Prize Corner - Item Prize 1"] = prize_rule
|
||||
item_rules["Celadon Prize Corner - Item Prize 2"] = prize_rule
|
||||
item_rules["Celadon Prize Corner - Item Prize 3"] = prize_rule
|
||||
|
||||
if world.accessibility[player] != "locations":
|
||||
world.get_location("Cerulean City - Bicycle Shop", player).always_allow = (lambda state, item:
|
||||
item.name == "Bike Voucher"
|
||||
and item.player == player)
|
||||
world.get_location("Fuchsia City - Safari Zone Warden", player).always_allow = (lambda state, item:
|
||||
item.name == "Gold Teeth" and
|
||||
item.player == player)
|
||||
|
||||
access_rules = {
|
||||
|
||||
"Pallet Town - Rival's Sister": lambda state: state.has("Oak's Parcel", player),
|
||||
"Pallet Town - Oak's Post-Route-22-Rival Gift": lambda state: state.has("Oak's Parcel", player),
|
||||
"Viridian City - Sleepy Guy": lambda state: state.pokemon_rb_can_cut(player) or state.pokemon_rb_can_surf(player),
|
||||
"Route 2 - Oak's Aide": lambda state: state.pokemon_rb_oaks_aide(state.multiworld.oaks_aide_rt_2[player].value + 5, player),
|
||||
"Pewter City - Museum": lambda state: state.pokemon_rb_can_cut(player),
|
||||
"Cerulean City - Bicycle Shop": lambda state: state.has("Bike Voucher", player),
|
||||
"Cerulean City - Bicycle Shop": lambda state: state.has("Bike Voucher", player)
|
||||
or item_name(state, "Cerulean City - Bicycle Shop", player) == ("Bike Voucher", player),
|
||||
"Lavender Town - Mr. Fuji": lambda state: state.has("Fuji Saved", player),
|
||||
"Vermilion Gym - Lt. Surge 1": lambda state: state.pokemon_rb_can_cut(player or state.pokemon_rb_can_surf(player)),
|
||||
"Vermilion Gym - Lt. Surge 2": lambda state: state.pokemon_rb_can_cut(player or state.pokemon_rb_can_surf(player)),
|
||||
"Route 11 - Oak's Aide": lambda state: state.pokemon_rb_oaks_aide(state.multiworld.oaks_aide_rt_11[player].value + 5, player),
|
||||
"Celadon City - Stranded Man": lambda state: state.pokemon_rb_can_surf(player),
|
||||
"Silph Co 11F - Silph Co President": lambda state: state.has("Card Key", player),
|
||||
"Fuchsia City - Safari Zone Warden": lambda state: state.has("Gold Teeth", player),
|
||||
"Silph Co 11F - Silph Co President (Card Key)": lambda state: state.has("Card Key", player),
|
||||
"Fuchsia City - Safari Zone Warden": lambda state: state.has("Gold Teeth", player)
|
||||
or item_name(state, "Fuchsia City - Safari Zone Warden", player) == ("Gold Teeth", player),
|
||||
"Route 12 - Island Item": lambda state: state.pokemon_rb_can_surf(player),
|
||||
"Route 12 - Item Behind Cuttable Tree": lambda state: state.pokemon_rb_can_cut(player),
|
||||
"Route 15 - Oak's Aide": lambda state: state.pokemon_rb_oaks_aide(state.multiworld.oaks_aide_rt_15[player].value + 5, player),
|
||||
|
@ -38,6 +57,23 @@ def set_rules(world, player):
|
|||
"Silph Co 6F - Southwest Item (Card Key)": lambda state: state.has("Card Key", player),
|
||||
"Silph Co 7F - East Item (Card Key)": lambda state: state.has("Card Key", player),
|
||||
"Safari Zone Center - Island Item": lambda state: state.pokemon_rb_can_surf(player),
|
||||
"Celadon Prize Corner - Item Prize 1": lambda state: state.has("Coin Case", player),
|
||||
"Celadon Prize Corner - Item Prize 2": lambda state: state.has("Coin Case", player),
|
||||
"Celadon Prize Corner - Item Prize 3": lambda state: state.has("Coin Case", player),
|
||||
"Celadon Game Corner - West Gambler's Gift (Coin Case)": lambda state: state.has("Coin Case", player),
|
||||
"Celadon Game Corner - Center Gambler's Gift (Coin Case)": lambda state: state.has("Coin Case", player),
|
||||
"Celadon Game Corner - East Gambler's Gift (Coin Case)": lambda state: state.has("Coin Case", player),
|
||||
"Celadon Game Corner - Hidden Item Northwest By Counter (Coin Case)": lambda state: state.has("Coin Case", player),
|
||||
"Celadon Game Corner - Hidden Item Southwest Corner (Coin Case)": lambda state: state.has("Coin Case", player),
|
||||
"Celadon Game Corner - Hidden Item Near Rumor Man (Coin Case)": lambda state: state.has("Coin Case", player),
|
||||
"Celadon Game Corner - Hidden Item Near Speculating Woman (Coin Case)": lambda state: state.has("Coin Case", player),
|
||||
"Celadon Game Corner - Hidden Item Near West Gifting Gambler (Coin Case)": lambda state: state.has("Coin Case", player),
|
||||
"Celadon Game Corner - Hidden Item Near Wonderful Time Woman (Coin Case)": lambda state: state.has("Coin Case", player),
|
||||
"Celadon Game Corner - Hidden Item Near Failing Gym Information Guy (Coin Case)": lambda state: state.has( "Coin Case", player),
|
||||
"Celadon Game Corner - Hidden Item Near East Gifting Gambler (Coin Case)": lambda state: state.has("Coin Case", player),
|
||||
"Celadon Game Corner - Hidden Item Near Hooked Guy (Coin Case)": lambda state: state.has("Coin Case", player),
|
||||
"Celadon Game Corner - Hidden Item at End of Horizontal Machine Row (Coin Case)": lambda state: state.has("Coin Case", player),
|
||||
"Celadon Game Corner - Hidden Item in Front of Horizontal Machine Row (Coin Case)": lambda state: state.has("Coin Case", player),
|
||||
|
||||
"Silph Co 11F - Silph Co Liberated": lambda state: state.has("Card Key", player),
|
||||
|
||||
|
@ -89,6 +125,16 @@ def set_rules(world, player):
|
|||
"Seafoam Islands B4F - Legendary Pokemon": lambda state: state.pokemon_rb_can_strength(player),
|
||||
"Vermilion City - Legendary Pokemon": lambda state: state.pokemon_rb_can_surf(player) and state.has("S.S. Ticket", player),
|
||||
|
||||
"Route 2 - Marcel Trade": lambda state: state.can_reach("Route 24 - Wild Pokemon - 6", "Location", player),
|
||||
"Underground Tunnel West-East - Spot Trade": lambda state: state.can_reach("Route 24 - Wild Pokemon - 6", "Location", player),
|
||||
"Route 11 - Terry Trade": lambda state: state.can_reach("Safari Zone Center - Wild Pokemon - 5", "Location", player),
|
||||
"Route 18 - Marc Trade": lambda state: state.can_reach("Route 23 - Super Rod Pokemon - 1", "Location", player),
|
||||
"Cinnabar Island - Sailor Trade": lambda state: state.can_reach("Pokemon Mansion 1F - Wild Pokemon - 3", "Location", player),
|
||||
"Cinnabar Island - Crinkles Trade": lambda state: state.can_reach("Route 12 - Wild Pokemon - 4", "Location", player),
|
||||
"Cinnabar Island - Doris Trade": lambda state: state.can_reach("Cerulean Cave 1F - Wild Pokemon - 9", "Location", player),
|
||||
"Vermilion City - Dux Trade": lambda state: state.can_reach("Route 3 - Wild Pokemon - 2", "Location", player),
|
||||
"Cerulean City - Lola Trade": lambda state: state.can_reach("Route 10 - Super Rod Pokemon - 1", "Location", player),
|
||||
|
||||
# Pokédex check
|
||||
"Pallet Town - Oak's Parcel Reward": lambda state: state.has("Oak's Parcel", player),
|
||||
|
||||
|
@ -142,7 +188,7 @@ def set_rules(world, player):
|
|||
"Pokemon Mansion 1F - Hidden Item Block Near Entrance Carpet": lambda
|
||||
state: state.pokemon_rb_can_get_hidden_items(player),
|
||||
"Pokemon Mansion 3F - Hidden Item Behind Burglar": lambda state: state.pokemon_rb_can_get_hidden_items(player),
|
||||
"Route 23 - Hidden Item Rocks Before Final Guard": lambda state: state.pokemon_rb_can_get_hidden_items(
|
||||
"Route 23 - Hidden Item Rocks Before Victory Road": lambda state: state.pokemon_rb_can_get_hidden_items(
|
||||
player),
|
||||
"Route 23 - Hidden Item East Bush After Water": lambda state: state.pokemon_rb_can_get_hidden_items(
|
||||
player),
|
||||
|
@ -178,3 +224,11 @@ def set_rules(world, player):
|
|||
for loc in world.get_locations(player):
|
||||
if loc.name in access_rules:
|
||||
add_rule(loc, access_rules[loc.name])
|
||||
if loc.name in item_rules:
|
||||
add_item_rule(loc, item_rules[loc.name])
|
||||
if loc.name.startswith("Pokedex"):
|
||||
mon = loc.name.split(" - ")[1]
|
||||
add_rule(loc, lambda state, i=mon: (state.has("Pokedex", player) or not
|
||||
state.multiworld.require_pokedex[player]) and (state.has(i, player)
|
||||
or state.has(f"Static {i}", player)))
|
||||
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
special_chars = {
|
||||
"PKMN": 0x4A,
|
||||
"LINE": 0x4F,
|
||||
"CONT": 0x55,
|
||||
"DONE": 0x57,
|
||||
"PROMPT": 0x58,
|
||||
"'d": 0xBB,
|
||||
"'l": 0xBC,
|
||||
"'t": 0xBE,
|
||||
|
@ -105,7 +109,7 @@ char_map = {
|
|||
"9": 0xFF,
|
||||
}
|
||||
|
||||
unsafe_chars = ["@", "#", "PKMN"]
|
||||
unsafe_chars = ["@", "#", "PKMN", "LINE", "DONE", "CONT", "PROMPT"]
|
||||
|
||||
|
||||
def encode_text(text: str, length: int=0, whitespace=False, force=False, safety=False):
|
||||
|
|
|
@ -509,7 +509,7 @@ class SMZ3World(World):
|
|||
return self.smz3DungeonItems
|
||||
else:
|
||||
return []
|
||||
|
||||
|
||||
def post_fill(self):
|
||||
# some small or big keys (those always_allow) can be unreachable in-game
|
||||
# while logic still collects some of them (probably to simulate the player collecting pot keys in the logic), some others don't
|
||||
|
@ -524,7 +524,7 @@ class SMZ3World(World):
|
|||
loc.item.classification = ItemClassification.filler
|
||||
loc.item.item.Progression = False
|
||||
loc.item.location.event = False
|
||||
self.unreachable.append(loc)
|
||||
self.unreachable.append(loc)
|
||||
|
||||
def get_filler_item_name(self) -> str:
|
||||
return self.multiworld.random.choice(self.junkItemsNames)
|
||||
|
|
Loading…
Reference in New Issue