mirror of
https://github.com/infinitefusion/infinitefusion-e18.git
synced 2026-11-03 08:15:47 +00:00
474 lines
14 KiB
Ruby
474 lines
14 KiB
Ruby
#PIF2 Safari Zone takes place entirely in the overworld, no battles. You throw Pokemon like you do in Legends Arceus.
|
|
# - Safari Balls only work if a Pokemon doesn't see you and the catch rate improves the closer you are to a Pokemon.
|
|
# - Catching bonus if the Pokemon hasn't seen you before (caught off guard)
|
|
# - You can place berries to attract Pokemon and distract them.
|
|
|
|
#
|
|
# Can hold the throw button to throw further (?)
|
|
#
|
|
|
|
TEMPLATE_EVENT_SAFARI_BALL = 15
|
|
TEMPLATE_EVENT_POKEBLOCK = 16
|
|
|
|
HOENN_SAFARI_MAPS = [MAP_SAFARI_1,MAP_SAFARI_2,MAP_SAFARI_3,MAP_SAFARI_4,MAP_SAFARI_5,MAP_SAFARI_6]
|
|
|
|
class PokemonTemp
|
|
attr_accessor :overworld_safari
|
|
end
|
|
|
|
class SafariState
|
|
attr_accessor :caught_pokemon
|
|
attr_accessor :area_types
|
|
attr_accessor :area_unlock_methods
|
|
|
|
end
|
|
|
|
def initialize_safari
|
|
nb_balls = pbGet(VAR_HOENN_SAFARI_NB_BALLS)
|
|
nb_balls = 18 if nb_balls <= 0
|
|
|
|
pbSafariState.pbStart(nb_balls)
|
|
$PokemonTemp.overworld_safari = true
|
|
updateSafariBallDisplay()
|
|
pbSafariState.area_types= initialize_area_types
|
|
pbSafariState.area_unlock_methods= initialize_unlock_methods
|
|
respawn_safari_ball_piles
|
|
end
|
|
|
|
def initialize_area_types
|
|
return {
|
|
:wetland => [
|
|
[:same_species,nil,3],
|
|
],
|
|
:lakeland => [[:different_species,:WATER,5]],
|
|
:badland => [[:different_species,:NORMAL,6]],
|
|
:grassland => [[:different_species,:GROUND,5]],
|
|
:woodland => [[:different_species,nil,20]],
|
|
:mountains => [[:different_species,:ELECTRIC,5]],
|
|
}
|
|
end
|
|
|
|
def initialize_unlock_methods
|
|
unlock_methods = {}
|
|
pbSafariState.area_types.each do |type, methods|
|
|
chosen_method = rand(methods.size)
|
|
unlock_methods[type] = chosen_method
|
|
end
|
|
echoln unlock_methods
|
|
return unlock_methods
|
|
end
|
|
|
|
def get_area_name(area_id)
|
|
case area_id
|
|
when :lakeland
|
|
return _INTL("Lake Zone")
|
|
when :wetland
|
|
return _INTL("Wetland Zone")
|
|
when :badland
|
|
return _INTL("Badland Zone")
|
|
when :grassland
|
|
return _INTL("Grassland Zone")
|
|
when :woodland
|
|
return _INTL("Woodland Zone")
|
|
when :mountains
|
|
return _INTL("Mountains Zone")
|
|
end
|
|
|
|
end
|
|
|
|
# Returns how many catches count toward the requirement.
|
|
# :different_species => number of distinct species (optionally only of the given type)
|
|
# :same_species => highest number of catches of any single species (optionally only of the given type)
|
|
def count_safari_caught(method_type, type = nil)
|
|
caught = pbSafariState.caught_pokemon || []
|
|
caught = caught.select { |s| GameData::Species.get(s).hasType?(type) } if type
|
|
|
|
case method_type
|
|
when :same_species
|
|
return caught.group_by { |species| species }.values.map(&:size).max || 0
|
|
else # :different_species
|
|
return caught.uniq.size
|
|
end
|
|
end
|
|
|
|
def count_safari_species(compared_species)
|
|
nb = 0
|
|
caught = pbSafariState.caught_pokemon || []
|
|
caught.each do |caught_species|
|
|
if caught_species == compared_species
|
|
nb += 1
|
|
end
|
|
end
|
|
return nb
|
|
end
|
|
|
|
def safari_requirement_text(method_type, type_id, nb_to_catch)
|
|
type_name = type_id ? GameData::Type.get(type_id).name : nil
|
|
|
|
case method_type
|
|
when :different_species
|
|
if type_name
|
|
return _INTL("I can only let you through if you've caught at least \\C[1]{1}\\C[0] different \\C[1]{2}-type\\C[0] Pokémon on this Safari.", nb_to_catch, type_name)
|
|
else
|
|
return _INTL("I can only let you through if you've caught at least \\C[1]{1}\\C[0] different species of Pokémon on this Safari.", nb_to_catch)
|
|
end
|
|
when :same_species
|
|
if type_name
|
|
return _INTL("I can only let you through if you've caught at least \\C[1]{1}\\C[0] \\C[1]{2}-type\\C[0] Pokémon of the same species on this Safari.", nb_to_catch, type_name)
|
|
else
|
|
return _INTL("I can only let you through if you've caught at least \\C[1]{1}\\C[0] Pokémon of the same species on this Safari.", nb_to_catch)
|
|
end
|
|
end
|
|
end
|
|
|
|
def safariNewAreaCheck(area_id)
|
|
area_name = get_area_name(area_id)
|
|
|
|
unlock_methods = pbSafariState.area_types[area_id]
|
|
unlock_method = unlock_methods[pbSafariState.area_unlock_methods[area_id]]
|
|
method_type, type_id, nb_to_catch = unlock_method
|
|
|
|
pbCallBub(2, @event_id)
|
|
pbMessage(_INTL("Halt! This way is the \\C[1]{1}\\C[0].", area_name))
|
|
pbCallBub(2, @event_id)
|
|
pbMessage(safari_requirement_text(method_type, type_id, nb_to_catch))
|
|
|
|
icons = displayCaughtPokemonIcons
|
|
pbWait(40)
|
|
icons.dispose
|
|
|
|
nb_caught = count_safari_caught(method_type, type_id)
|
|
if nb_caught >= nb_to_catch
|
|
pbSEPlay("GUI naming confirm")
|
|
pbCallBub(2, @event_id)
|
|
pbMessage(_INTL("Yes! You've met the requirement. Go right ahead!"))
|
|
return true
|
|
else
|
|
pbSEPlay("GUI sel buzzer")
|
|
pbCallBub(2, @event_id)
|
|
pbMessage(_INTL("Oh, no! You've only got {1} so far... That's not enough!", nb_caught))
|
|
pbCallBub(2, @event_id)
|
|
pbMessage(_INTL("You need to catch more Pokémon before I can let you through!"))
|
|
return false
|
|
end
|
|
end
|
|
|
|
def updateSafariBallDisplay
|
|
return unless $PokemonTemp.overworld_safari
|
|
pbSafariState.updateBallDisplay
|
|
end
|
|
|
|
def throw_ow_safari_ball
|
|
template_event = TEMPLATE_EVENT_SAFARI_BALL
|
|
position = [$game_player.x, $game_player.y]
|
|
event = $PokemonTemp.createTempEvent(template_event, $game_map.map_id, position, $game_player.direction)
|
|
return event
|
|
end
|
|
|
|
def use_safari_feeder(price, floating=false)
|
|
discount = pbGet(VAR_HOENN_SAFARI_FEEDER_DISCOUNT)
|
|
price-=discount
|
|
if price > 0
|
|
message = _INTL("\\GWould you like to use the Pokéblock Feeder for ${1}?",price)
|
|
else
|
|
message = _INTL("\\GWould you like to use the Pokéblock Feeder?",price)
|
|
end
|
|
if pbConfirmMessage(message)
|
|
colors = [:red, :red, :red,
|
|
:blue, :blue, :blue,
|
|
:pink, :pink, :pink,
|
|
:green, :green, :green,
|
|
:yellow, :yellow, :yellow].sample(4)
|
|
pbSpendMoney(price) if price > 0
|
|
feeder_event = this_event()
|
|
positions = [[-1,0], [1,0], [0,1], [0,-1]]
|
|
i=0
|
|
template_event = TEMPLATE_EVENT_POKEBLOCK
|
|
|
|
colors.each do |color|
|
|
|
|
block_event=$PokemonTemp.createTempEvent(template_event,
|
|
$game_map.map_id,
|
|
[feeder_event.x,feeder_event.y],
|
|
$game_player.direction,
|
|
PokeblockEvent, [color,12])
|
|
if floating
|
|
block_event.floating = true
|
|
block_event.forced_bush_depth = 8
|
|
block_event.calculate_bush_depth
|
|
end
|
|
block_event.jump(*positions[i])
|
|
i+=1
|
|
end
|
|
end
|
|
end
|
|
|
|
def create_overworld_pokemon_event(pokemon, position, terrain, behavior_roaming = nil, behavior_noticed = nil)
|
|
template_event = TEMPLATE_EVENT_OW_POKEMON_NORMAL
|
|
|
|
species = pokemon[0]
|
|
level = pokemon[1]
|
|
event = $PokemonTemp.createTempEvent(template_event, $game_map.map_id, position, nil, DynamicOverworldPokemonEvent) do |event|
|
|
event.setup_pokemon(species, level, terrain, behavior_roaming, behavior_noticed)
|
|
end
|
|
return unless event
|
|
event.direction = [DIRECTION_LEFT, DIRECTION_RIGHT, DIRECTION_DOWN, DIRECTION_UP].sample
|
|
playOverworldPokemonSpawnAnimation(event, terrain)
|
|
return event
|
|
end
|
|
|
|
def safari_ball_check_collision(ball_event)
|
|
colliding_event = $game_map.get_event_at_position(ball_event.x, ball_event.y, [ball_event.id])
|
|
if colliding_event
|
|
if colliding_event.is_a?(OverworldPokemonEvent)
|
|
echoln "touched a Pokemon! #{colliding_event.name}"
|
|
ball_event.despawn
|
|
try_catch_safari_pokemon(colliding_event)
|
|
renew_overworld_pokemon(colliding_event.id)
|
|
else
|
|
pbSEPlay("MiningPick")
|
|
echoln "touched something that's not a Pokemon! #{colliding_event.name}"
|
|
ball_event.despawn
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
def release_caught_pokemon
|
|
allowed_pokemon = ($game_temp.last_safari_stats[0] || []).map(&:personalID)
|
|
filter = proc {|pokemon|
|
|
allowed_pokemon.include?(pokemon.personalID)
|
|
}
|
|
released = []
|
|
pbFadeOutIn {
|
|
scene = SafariReleaseStorageScene.new
|
|
scene.cursormode == "multiselect"
|
|
screen = SafariReleaseStorageScreen.new(scene, $PokemonStorage)
|
|
screen.setFilter(filter)
|
|
released = screen.pbStartScreen(0) # Boot PC in release only mode
|
|
}
|
|
nb_points = calculate_release_points(released)
|
|
return nb_points
|
|
echoln "Obtained #{nb_points} points for releasing Pokemon"
|
|
end
|
|
|
|
def calculate_catch_rate_points(rate)
|
|
case rate
|
|
when 0..45 then 4
|
|
when 45..55 then 3
|
|
when 55..90 then 2
|
|
when 90..200 then 1
|
|
else 0
|
|
end
|
|
end
|
|
|
|
#Base points: 2
|
|
# duplicate: -1
|
|
# Shiny: +10 points
|
|
#
|
|
def calculate_release_points(released_pokemon)
|
|
total_points = 0
|
|
species_counts = Hash.new(0)
|
|
released_pokemon.each { |p| species_counts[p.species] += 1 }
|
|
|
|
total_points = 0
|
|
released_pokemon.each do |pokemon|
|
|
species = GameData::Species.get(pokemon.species)
|
|
points = 2
|
|
catch_rate_points = calculate_catch_rate_points(species.catch_rate)
|
|
points += catch_rate_points
|
|
points += 10 if pokemon.shiny?
|
|
is_duplicate = species_counts[pokemon.species] > 1
|
|
points -= 1 if is_duplicate
|
|
total_points += points
|
|
echoln "calculating points for #{pokemon.species}: #{points}"
|
|
end
|
|
return total_points
|
|
end
|
|
|
|
|
|
class Trainer
|
|
attr_accessor :safari_caught
|
|
end
|
|
def record_safari_catch(pokemon)
|
|
pbSafariState.caught_pokemon = [] unless pbSafariState.caught_pokemon
|
|
pbSafariState.caught_pokemon << pokemon# unless pbSafariState.caught_pokemon.include?(pokemon.species)
|
|
end
|
|
|
|
ANIMATION_POKEBALL = 8
|
|
|
|
MAX_THROW_DISTANCE = 4
|
|
CATCH_RATE_FLOOR = 60
|
|
SNEAK_FLAT_BONUS = 50
|
|
def try_catch_safari_pokemon(pokemon_event)
|
|
pokemon_state = pokemon_event.current_state
|
|
playAnimation(ANIMATION_POKEBALL, pokemon_event.x, pokemon_event.y)
|
|
initial_name = pokemon_event.character_name
|
|
pokemon_event.character_name="safari_ball_throw"
|
|
pokemon_event.hide_notice_icon
|
|
pokemon = pokemon_event.pokemon
|
|
pokemon_event.lock
|
|
pbWait(14)
|
|
|
|
distance_from_player = pokemon_event.distance_from_player
|
|
unnoticed = !pokemon_event.noticed_player_once
|
|
|
|
distance = distance_from_player.clamp(1, MAX_THROW_DISTANCE)
|
|
closeness = (MAX_THROW_DISTANCE + 1 - distance) / MAX_THROW_DISTANCE.to_f
|
|
|
|
capture_rate = [pokemon.species_data.catch_rate, CATCH_RATE_FLOOR].max
|
|
|
|
multiplier = unnoticed ? 1 + (2 * closeness) : 1 + closeness
|
|
capture_rate = (capture_rate * multiplier) + (SNEAK_FLAT_BONUS * closeness)
|
|
|
|
capture_rate = 0 if pokemon_state == :NOTICED_PLAYER
|
|
|
|
echoln "base capture rate: #{pokemon.species_data.catch_rate}"
|
|
echoln "safari capture rate: #{capture_rate}"
|
|
echoln "-----"
|
|
num_shakes = pbCaptureCalc(pokemon, capture_rate, :SAFARIBALL)
|
|
|
|
shake = 0
|
|
num_shakes.times do
|
|
shake+=1
|
|
if shake < 4
|
|
pbSEPlay("ballshake")
|
|
else
|
|
pbSEPlay("Battle catch click")
|
|
end
|
|
|
|
pokemon_event.pattern=1
|
|
pbWait(2)
|
|
if shake >= 4
|
|
spritesLoader = BattleSpriteLoader.new
|
|
pokemon.pif_sprite = spritesLoader.obtain_pif_sprite(pokemon.species)
|
|
showPokemonInPokeballWithMessage(pokemon.pif_sprite, _INTL("{1} obtained {2}!\\me[Pkmn get]\\wtnp[80]\1",$Trainer.name,pokemon.name))
|
|
record_safari_catch(pokemon)
|
|
pbAddPokemonSilent(pokemon)
|
|
pokemon_event.despawn
|
|
return
|
|
end
|
|
pokemon_event.pattern=0
|
|
pbWait(24)
|
|
end
|
|
playAnimation(ANIMATION_POKEBALL, pokemon_event.x, pokemon_event.y)
|
|
pokemon_event.character_name = initial_name
|
|
pokemon_event.show_notice_icon
|
|
pokemon_event.unlock
|
|
end
|
|
|
|
|
|
class Event
|
|
def prepend(callback)
|
|
@callbacks.unshift(callback)
|
|
return self
|
|
end
|
|
end
|
|
|
|
|
|
Events.onAction.prepend(proc { |_sender, _e|
|
|
next unless Settings::HOENN && $PokemonTemp.overworld_safari
|
|
next if $game_player.jumping? || $game_player.moving?
|
|
next if $game_player.pbFacingEvent
|
|
next if $game_player.pbFacingTerrainTag.can_surf_freely && !$PokemonGlobal.surfing
|
|
|
|
next if pbSafariState.ballcount <= 0
|
|
pbSafariState.ballcount -= 1
|
|
updateSafariBallDisplay
|
|
ball_event = throw_ow_safari_ball
|
|
check_nb_safari_balls_left
|
|
})
|
|
|
|
def check_nb_safari_balls_left
|
|
echoln "#{pbSafariState.ballcount} balls left"
|
|
if pbSafariState.ballcount < 0 #when directly zero, the player can still run around and find another pile
|
|
pbMessage(_INTL("PA: Ding-dong!\1"))
|
|
pbMessage(_INTL("PA: Your safari game is over!"))
|
|
pbSafariState.decision = 1
|
|
pbSafariState.pbGoToStart
|
|
end
|
|
end
|
|
|
|
|
|
#Simplified copy of the method in PokeBattle to be used outside of a battle (safari zone)
|
|
def pbCaptureCalc(pkmn, catch_rate, ball)
|
|
battler = PokeBattle_FakeBattler.new(nil,0, pkmn)
|
|
return 4 if $DEBUG && Input.press?(Input::CTRL)
|
|
# Get a catch rate if one wasn't provided
|
|
catch_rate = pkmn.species_data.catch_rate if !catch_rate
|
|
|
|
# First half of the shakes calculation
|
|
a = battler.totalhp
|
|
b = battler.hp
|
|
x = ((3 * a - 2 * b) * catch_rate.to_f) / (3 * a)
|
|
# Calculation modifiers
|
|
if battler.status == :SLEEP || battler.status == :FROZEN
|
|
x *= 2.5
|
|
elsif battler.status != :NONE
|
|
x *= 1.5
|
|
end
|
|
x = x.floor
|
|
x = 1 if x < 1
|
|
# Definite capture, no need to perform randomness checks
|
|
return 4 if x >= 255 || BallHandlers.isUnconditional?(ball, self, battler)
|
|
# Second half of the shakes calculation
|
|
y = (65536 / ((255.0 / x) ** 0.1875)).floor
|
|
|
|
# Calculate the number of shakes
|
|
numShakes = 0
|
|
for i in 0...4
|
|
break if numShakes < i
|
|
numShakes += 1 if rand(65536) < y
|
|
end
|
|
return numShakes
|
|
end
|
|
|
|
|
|
def placePokeblock()
|
|
block = pbPokeblockCase(false)
|
|
pbRemovePokeblock(block)
|
|
return block
|
|
end
|
|
|
|
def safari_ball_pile(min_nb, max_nb)
|
|
if min_nb == max_nb
|
|
nb = min_nb
|
|
else
|
|
nb = rand(min_nb...max_nb)
|
|
end
|
|
pbSafariState.ballcount+=nb
|
|
pbMEPlay("Item get")
|
|
pbMessage(_INTL("{1} found {2} \\C[1]Safari Balls\\C[0]!", $Trainer.name, nb))
|
|
updateSafariBallDisplay()
|
|
end
|
|
|
|
def respawn_safari_ball_piles
|
|
HOENN_SAFARI_MAPS.each do |map_id|
|
|
map = $MapFactory.getMapNoAdd(map_id)
|
|
for event_data in map.events
|
|
event_id = event_data[0]
|
|
event = event_data[1]
|
|
if event.name == "SafariBalls"
|
|
pbSetSelfSwitch(event.id,"A",false,map_id)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
SWITCH_UNLOCKED_WOODLANDS = 2252
|
|
SWITCH_UNLOCKED_WETLANDS = 2253
|
|
SWITCH_UNLOCKED_BADLANDS = 2254
|
|
SWITCH_UNLOCKED_GRASSLANDS = 2255
|
|
SWITCH_UNLOCKED_LAKE = 2256
|
|
SWITCH_UNLOCKED_MOUNTAINS = 2257
|
|
|
|
def list_unlocked_zones
|
|
unlocked = []
|
|
unlocked << :woodland if $game_switches[SWITCH_UNLOCKED_WOODLANDS]
|
|
unlocked << :wetland if $game_switches[SWITCH_UNLOCKED_WETLANDS]
|
|
unlocked << :badland if $game_switches[SWITCH_UNLOCKED_BADLANDS]
|
|
unlocked << :grassland if $game_switches[SWITCH_UNLOCKED_GRASSLANDS]
|
|
unlocked << :lakeland if $game_switches[SWITCH_UNLOCKED_LAKE]
|
|
unlocked << :mountains if $game_switches[SWITCH_UNLOCKED_MOUNTAINS]
|
|
return unlocked
|
|
end |