diff --git a/Data/Scripts/003_Game classes/007_Game_Player.rb b/Data/Scripts/003_Game classes/007_Game_Player.rb index 00efc93d7..93a946eea 100644 --- a/Data/Scripts/003_Game classes/007_Game_Player.rb +++ b/Data/Scripts/003_Game classes/007_Game_Player.rb @@ -462,6 +462,7 @@ def pbMountBike pbUpdateVehicle bike_bgm = GameData::Metadata.get.bicycle_BGM pbCueBGM(bike_bgm, 0.5) if bike_bgm + pbPokeRadarCancel end def pbDismountBike diff --git a/Data/Scripts/011_Data/001_Data_Cache.rb b/Data/Scripts/011_Data/001_Data_Cache.rb index 41f1fa2a4..b22e25b3f 100644 --- a/Data/Scripts/011_Data/001_Data_Cache.rb +++ b/Data/Scripts/011_Data/001_Data_Cache.rb @@ -3,7 +3,6 @@ #=============================================================================== class PokemonTemp attr_accessor :townMapData - attr_accessor :encountersData attr_accessor :phoneData attr_accessor :regionalDexes attr_accessor :speciesShadowMovesets @@ -14,7 +13,6 @@ end def pbClearData if $PokemonTemp $PokemonTemp.townMapData = nil - $PokemonTemp.encountersData = nil $PokemonTemp.phoneData = nil $PokemonTemp.regionalDexes = nil $PokemonTemp.speciesShadowMovesets = nil @@ -42,19 +40,6 @@ def pbLoadTownMapData return $PokemonTemp.townMapData end -#=============================================================================== -# Method to get wild encounter data. -#=============================================================================== -def pbLoadEncountersData - $PokemonTemp = PokemonTemp.new if !$PokemonTemp - if !$PokemonTemp.encountersData - if pbRgssExists?("Data/encounters.dat") - $PokemonTemp.encountersData = load_data("Data/encounters.dat") - end - end - return $PokemonTemp.encountersData -end - #=============================================================================== # Method to get phone call data. #=============================================================================== diff --git a/Data/Scripts/011_Data/001_Game data/001_Game data.rb b/Data/Scripts/011_Data/001_Game data/001_Game data.rb index 45e2f8937..5a637757b 100644 --- a/Data/Scripts/011_Data/001_Game data/001_Game data.rb +++ b/Data/Scripts/011_Data/001_Game data/001_Game data.rb @@ -40,6 +40,7 @@ module GameData return (self::DATA.has_key?(other)) ? self::DATA[other] : nil end + # Yields all data in order of their id_number. def each keys = self::DATA.keys.sort { |a, b| self::DATA[a].id_number <=> self::DATA[b].id_number } keys.each { |key| yield self::DATA[key] if !key.is_a?(Integer) } @@ -130,5 +131,6 @@ module GameData Type.load Species.load Trainer.load + Encounter.load end end diff --git a/Data/Scripts/011_Data/001_Game data/013_Encounter.rb b/Data/Scripts/011_Data/001_Game data/013_Encounter.rb new file mode 100644 index 000000000..d018ed8dd --- /dev/null +++ b/Data/Scripts/011_Data/001_Game data/013_Encounter.rb @@ -0,0 +1,78 @@ +# $PokemonGlobal.encounter_version + +module GameData + class Encounter + attr_accessor :id + attr_accessor :map + attr_accessor :version + attr_reader :step_chances + attr_reader :types + + DATA = {} + DATA_FILENAME = "encounters.dat" + + extend ClassMethods + include InstanceMethods + + # @param map_id [Integer] + # @param map_version [Integer] + # @return [Boolean] whether there is encounter data for the given map ID/version + def self.exists?(map_id, map_version = 0) + validate map_id => [Integer] + validate map_version => [Integer] + key = sprintf("%s_%d", map_id, map_version).to_sym + return !self::DATA[key].nil? + end + + # @param map_id [Integer] + # @param version [Integer] + # @return [self, nil] + def self.get(map_id, map_version = 0) + validate map_id => Integer + validate map_version => Integer + trial_key = sprintf("%s_%d", map_id, map_version).to_sym + key = (self::DATA.has_key?(trial_key)) ? trial_key : sprintf("%s_0", map_id).to_sym + return self::DATA[key] + end + + # Yields all encounter data in order of their map and version numbers. + def self.each + keys = self::DATA.keys.sort do |a, b| + if self::DATA[a].map == self::DATA[b].map + self::DATA[a].version <=> self::DATA[b].version + else + self::DATA[a].map <=> self::DATA[b].map + end + end + keys.each { |key| yield self::DATA[key] } + end + + # Yields all encounter data for the given version. Also yields encounter + # data for version 0 of a map if that map doesn't have encounter data for + # the given version. + def self.each_of_version(version = 0) + self.each do |data| + yield data if data.version == version + if version > 0 + yield data if data.version == 0 && !self::DATA.has_key?([data.map, version]) + end + end + end + + def initialize(hash) + @id = hash[:id] + @map = hash[:map] + @version = hash[:version] || 0 + @step_chances = hash[:step_chances] + @types = hash[:types] || [] + end + end +end + +#=============================================================================== +# Deprecated methods +#=============================================================================== +def pbLoadEncountersData + Deprecation.warn_method('pbLoadEncountersData', 'v20', 'GameData::Encounter.get(map_id, version)') + return nil +end diff --git a/Data/Scripts/013_Overworld/002_PField_Field.rb b/Data/Scripts/013_Overworld/002_PField_Field.rb index f79613c4b..084ad9f5b 100644 --- a/Data/Scripts/013_Overworld/002_PField_Field.rb +++ b/Data/Scripts/013_Overworld/002_PField_Field.rb @@ -361,34 +361,32 @@ def pbOnStepTaken(eventTriggered) $PokemonGlobal.stepcount = 0 if !$PokemonGlobal.stepcount $PokemonGlobal.stepcount += 1 $PokemonGlobal.stepcount &= 0x7FFFFFFF - repel = ($PokemonGlobal.repel>0) Events.onStepTaken.trigger(nil) # Events.onStepTakenFieldMovement.trigger(nil,$game_player) handled = [nil] Events.onStepTakenTransferPossible.trigger(nil,handled) return if handled[0] - pbBattleOnStepTaken(repel) if !eventTriggered && !$game_temp.in_menu - $PokemonTemp.encounterTriggered = false # This info isn't needed + pbBattleOnStepTaken if !eventTriggered && !$game_temp.in_menu + $PokemonTemp.encounterTriggered = false # This info isn't needed here end # Start wild encounters while turning on the spot Events.onChangeDirection += proc { - repel = ($PokemonGlobal.repel > 0) - pbBattleOnStepTaken(repel) if !$game_temp.in_menu + pbBattleOnStepTaken if !$game_temp.in_menu } -def pbBattleOnStepTaken(repel = false) +def pbBattleOnStepTaken return if $Trainer.able_pokemon_count == 0 - encounterType = $PokemonEncounters.pbEncounterType + return if !$PokemonEncounters.encounter_possible_here? + encounterType = $PokemonEncounters.encounter_type return if encounterType < 0 - return if !$PokemonEncounters.isEncounterPossibleHere? + return if !$PokemonEncounter.step_triggers_encounter?(encounterType) $PokemonTemp.encounterType = encounterType - encounter = $PokemonEncounters.pbGenerateEncounter(encounterType) + encounter = $PokemonEncounters.choose_wild_pokemon(encounterType) encounter = EncounterModifier.trigger(encounter) - if $PokemonEncounters.pbCanEncounter?(encounter, repel) - if !$PokemonTemp.forceSingleBattle && !pbInSafari? && ($PokemonGlobal.partner || - ($Trainer.able_pokemon_count > 1 && PBTerrain.isDoubleWildBattle?(pbGetTerrainTag) && rand(100) < 30)) - encounter2 = $PokemonEncounters.pbEncounteredPokemon(encounterType) + if $PokemonEncounter.allow_encounter?(encounter) + if $PokemonEncounter.have_double_wild_battle? + encounter2 = $PokemonEncounters.choose_wild_pokemon(encounterType) encounter2 = EncounterModifier.trigger(encounter2) pbDoubleWildBattle(encounter[0], encounter[1], encounter2[0], encounter2[1]) else @@ -851,7 +849,7 @@ def pbJumpToward(dist=1,playSound=false,cancelSurf=false) end if $game_player.x!=x || $game_player.y!=y pbSEPlay("Player jump") if playSound - $PokemonEncounters.clearStepCount if cancelSurf + $PokemonEncounters.reset_step_count if cancelSurf $PokemonTemp.endSurf = true if cancelSurf while $game_player.jumping? Graphics.update diff --git a/Data/Scripts/013_Overworld/003_PField_Visuals.rb b/Data/Scripts/013_Overworld/003_PField_Visuals.rb index 29ea1563a..265e1c516 100644 --- a/Data/Scripts/013_Overworld/003_PField_Visuals.rb +++ b/Data/Scripts/013_Overworld/003_PField_Visuals.rb @@ -44,7 +44,7 @@ def pbBattleAnimation(bgm=nil,battletype=0,foe=nil) $PokemonTemp.encounterType==EncounterTypes::GoodRod || $PokemonTemp.encounterType==EncounterTypes::SuperRod) location = 3 - elsif $PokemonEncounters.isCave? + elsif $PokemonEncounters.has_cave_encounters? location = 2 elsif !GameData::MapMetadata.exists?($game_map.map_id) || !GameData::MapMetadata.get($game_map.map_id).outdoor_map @@ -112,7 +112,7 @@ def pbBattleAnimation(bgm=nil,battletype=0,foe=nil) $PokemonGlobal.nextBattleME = nil $PokemonGlobal.nextBattleCaptureME = nil $PokemonGlobal.nextBattleBack = nil - $PokemonEncounters.clearStepCount + $PokemonEncounters.reset_step_count # Fade back to the overworld viewport.color = Color.new(0,0,0,255) numFrames = Graphics.frame_rate*4/10 # 0.4 seconds, 16 frames @@ -134,10 +134,10 @@ def pbBattleAnimationOverride(viewport,battletype=0,foe=nil) tr_type = foe[0].trainer_type tr_type_id = GameData::TrainerType.get(tr_type).id_number if tr_type - tbargraphic = sprintf("Graphics/Transitions/vsBar%s", tr_type.to_s) rescue nil - tbargraphic = sprintf("Graphics/Transitions/vsBar%d", tr_type_id) if !pbResolveBitmap(tbargraphic) - tgraphic = sprintf("Graphics/Transitions/vsTrainer%s", tr_type.to_s) rescue nil - tgraphic = sprintf("Graphics/Transitions/vsTrainer%d", tr_type_id) if !pbResolveBitmap(tgraphic) + tbargraphic = sprintf("Graphics/Transitions/vsBar_%s", tr_type.to_s) rescue nil + tbargraphic = sprintf("Graphics/Transitions/vsBar_%d", tr_type_id) if !pbResolveBitmap(tbargraphic) + tgraphic = sprintf("Graphics/Transitions/vsTrainer_%s", tr_type.to_s) rescue nil + tgraphic = sprintf("Graphics/Transitions/vsTrainer_%d", tr_type_id) if !pbResolveBitmap(tgraphic) if pbResolveBitmap(tbargraphic) && pbResolveBitmap(tgraphic) player_tr_type = $Trainer.trainer_type player_tr_type_id = GameData::TrainerType.get(player_tr_type).id_number @@ -156,11 +156,11 @@ def pbBattleAnimationOverride(viewport,battletype=0,foe=nil) overlay = Sprite.new(viewport) overlay.bitmap = Bitmap.new(Graphics.width,Graphics.height) pbSetSystemFont(overlay.bitmap) - pbargraphic = sprintf("Graphics/Transitions/vsBar%s_%d", player_tr_type.to_s, outfit) rescue nil - pbargraphic = sprintf("Graphics/Transitions/vsBar%d_%d", player_tr_type_id, outfit) if !pbResolveBitmap(pbargraphic) + pbargraphic = sprintf("Graphics/Transitions/vsBar_%s_%d", player_tr_type.to_s, outfit) rescue nil + pbargraphic = sprintf("Graphics/Transitions/vsBar_%d_%d", player_tr_type_id, outfit) if !pbResolveBitmap(pbargraphic) if !pbResolveBitmap(pbargraphic) - pbargraphic = sprintf("Graphics/Transitions/vsBar%s", player_tr_type.to_s) rescue nil - pbargraphic = sprintf("Graphics/Transitions/vsBar%d", player_tr_type_id) if !pbResolveBitmap(pbargraphic) + pbargraphic = sprintf("Graphics/Transitions/vsBar_%s", player_tr_type.to_s) rescue nil + pbargraphic = sprintf("Graphics/Transitions/vsBar_%d", player_tr_type_id) if !pbResolveBitmap(pbargraphic) end xoffset = ((Graphics.width/2)/10)*10 bar1 = Sprite.new(viewplayer) @@ -197,11 +197,11 @@ def pbBattleAnimationOverride(viewport,battletype=0,foe=nil) bar1.bitmap = BitmapCache.load_bitmap(pbargraphic) bar2 = AnimatedPlane.new(viewopp) bar2.bitmap = BitmapCache.load_bitmap(tbargraphic) - pgraphic = sprintf("Graphics/Transitions/vsTrainer%s_%d", player_tr_type.to_s, outfit) rescue nil - pgraphic = sprintf("Graphics/Transitions/vsTrainer%d_%d", player_tr_type_id, outfit) if !pbResolveBitmap(pgraphic) + pgraphic = sprintf("Graphics/Transitions/vsTrainer_%s_%d", player_tr_type.to_s, outfit) rescue nil + pgraphic = sprintf("Graphics/Transitions/vsTrainer_%d_%d", player_tr_type_id, outfit) if !pbResolveBitmap(pgraphic) if !pbResolveBitmap(pgraphic) - pgraphic = sprintf("Graphics/Transitions/vsTrainer%s", player_tr_type.to_s) rescue nil - pgraphic = sprintf("Graphics/Transitions/vsTrainer%d", player_tr_type_id) if !pbResolveBitmap(pgraphic) + pgraphic = sprintf("Graphics/Transitions/vsTrainer_%s", player_tr_type.to_s) rescue nil + pgraphic = sprintf("Graphics/Transitions/vsTrainer_%d", player_tr_type_id) if !pbResolveBitmap(pgraphic) end player = Sprite.new(viewplayer) player.bitmap = BitmapCache.load_bitmap(pgraphic) diff --git a/Data/Scripts/013_Overworld/005_PField_Metadata.rb b/Data/Scripts/013_Overworld/005_PField_Metadata.rb index a09197307..599861f5c 100644 --- a/Data/Scripts/013_Overworld/005_PField_Metadata.rb +++ b/Data/Scripts/013_Overworld/005_PField_Metadata.rb @@ -46,6 +46,7 @@ class PokemonGlobalMetadata attr_writer :bridge attr_accessor :repel attr_accessor :flashUsed + attr_accessor :encounter_version # Map transfers attr_accessor :healingSpot attr_accessor :escapePoint @@ -111,6 +112,7 @@ class PokemonGlobalMetadata @bridge = 0 @repel = 0 @flashused = false + @encounter_version = 0 # Map transfers @healingSpot = nil @escapePoint = [] diff --git a/Data/Scripts/013_Overworld/007_PField_Encounters.rb b/Data/Scripts/013_Overworld/007_PField_Encounters.rb index fabfd52b6..5eeb8198d 100644 --- a/Data/Scripts/013_Overworld/007_PField_Encounters.rb +++ b/Data/Scripts/013_Overworld/007_PField_Encounters.rb @@ -1,49 +1,96 @@ module EncounterTypes - Land = 0 - Cave = 1 - Water = 2 - RockSmash = 3 - OldRod = 4 - GoodRod = 5 - SuperRod = 6 - HeadbuttLow = 7 - HeadbuttHigh = 8 - LandMorning = 9 - LandDay = 10 - LandNight = 11 - BugContest = 12 + Land = 0 + LandDay = 1 + LandNight = 2 + LandMorning = 3 + LandAfternoon = 4 + LandEvening = 5 + Cave = 6 + CaveDay = 7 + CaveNight = 8 + CaveMorning = 9 + CaveAfternoon = 10 + CaveEvening = 11 + Water = 12 + WaterDay = 13 + WaterNight = 14 + WaterMorning = 15 + WaterAfternoon = 16 + WaterEvening = 17 + OldRod = 18 + GoodRod = 19 + SuperRod = 20 + RockSmash = 21 + HeadbuttLow = 22 + HeadbuttHigh = 23 + BugContest = 24 + Names = [ - "Land", - "Cave", - "Water", - "RockSmash", - "OldRod", - "GoodRod", - "SuperRod", - "HeadbuttLow", - "HeadbuttHigh", - "LandMorning", - "LandDay", - "LandNight", - "BugContest" + "Land", "LandDay", "LandNight", "LandMorning", "LandAfternoon", "LandEvening", + "Cave", "CaveDay", "CaveNight", "CaveMorning", "CaveAfternoon", "CaveEvening", + "Water", "WaterDay", "WaterNight", "WaterMorning", "WaterAfternoon", "WaterEvening", + "OldRod", "GoodRod", "SuperRod", "RockSmash", "HeadbuttLow", "HeadbuttHigh", + "BugContest" ] - EnctypeChances = [ - [20,20,10,10,10,10,5,5,4,4,1,1], - [20,20,10,10,10,10,5,5,4,4,1,1], - [60,30,5,4,1], - [60,30,5,4,1], - [70,30], - [60,20,20], - [40,40,15,4,1], - [30,25,20,10,5,5,4,1], - [30,25,20,10,5,5,4,1], - [20,20,10,10,10,10,5,5,4,4,1,1], - [20,20,10,10,10,10,5,5,4,4,1,1], - [20,20,10,10,10,10,5,5,4,4,1,1], - [20,20,10,10,10,10,5,5,4,4,1,1] + Probabilities = [ + [20, 20, 10, 10, 10, 10, 5, 5, 4, 4, 1, 1], + [20, 20, 10, 10, 10, 10, 5, 5, 4, 4, 1, 1], + [20, 20, 10, 10, 10, 10, 5, 5, 4, 4, 1, 1], + [20, 20, 10, 10, 10, 10, 5, 5, 4, 4, 1, 1], + [20, 20, 10, 10, 10, 10, 5, 5, 4, 4, 1, 1], + [20, 20, 10, 10, 10, 10, 5, 5, 4, 4, 1, 1], + [20, 20, 10, 10, 10, 10, 5, 5, 4, 4, 1, 1], + [20, 20, 10, 10, 10, 10, 5, 5, 4, 4, 1, 1], + [20, 20, 10, 10, 10, 10, 5, 5, 4, 4, 1, 1], + [20, 20, 10, 10, 10, 10, 5, 5, 4, 4, 1, 1], + [20, 20, 10, 10, 10, 10, 5, 5, 4, 4, 1, 1], + [20, 20, 10, 10, 10, 10, 5, 5, 4, 4, 1, 1], + [60, 30, 5, 4, 1], + [60, 30, 5, 4, 1], + [60, 30, 5, 4, 1], + [60, 30, 5, 4, 1], + [60, 30, 5, 4, 1], + [60, 30, 5, 4, 1], + [70, 30], + [60, 20, 20], + [40, 40, 15, 4, 1], + [60, 30, 5, 4, 1], + [30, 25, 20, 10, 5, 5, 4, 1], + [30, 25, 20, 10, 5, 5, 4, 1], + [20, 20, 10, 10, 10, 10, 5, 5, 4, 4, 1, 1] ] - EnctypeDensities = [25, 10, 10, 0, 0, 0, 0, 0, 0, 25, 25, 25, 25] - EnctypeCompileDens = [ 1, 2, 3, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1] + Chances_Per_Step = [ + 25, 25, 25, 25, 25, 25, # Lands + 10, 10, 10, 10, 10, 10, # Caves + 10, 10, 10, 10, 10, 10, # Waters + 0, 0, 0, 0, 0, 0, 25 + ] + Kinds = [ + 1, 1, 1, 1, 1, 1, # Lands + 2, 2, 2, 2, 2, 2, # Caves + 3, 3, 3, 3, 3, 3, # Waters + 0, 0, 0, 0, 0, 0, 1 + ] + + def self.is_land_type?(enc_type) + return self.is_normal_land_type?(enc_type) || enc_type == BugContest + end + + def self.is_normal_land_type?(enc_type) + return [Land, LandDay, LandNight, LandMorning, LandAfternoon, LandEvening].include?(enc_type) + end + + def self.is_cave_type?(enc_type) + return [Cave, CaveDay, CaveNight, CaveMorning, CaveAfternoon, CaveEvening].include?(enc_type) + end + + def self.is_water_type?(enc_type) + return [Water, WaterDay, WaterNight, WaterMorning, WaterAfternoon, WaterEvening].include?(enc_type) + end + + def self.is_fishing_type?(enc_type) + return [OldRod, GoodRod, SuperRod].include?(enc_type) + end end @@ -52,306 +99,143 @@ end # #=============================================================================== class PokemonEncounters - attr_reader :stepcount + attr_reader :step_count def initialize - @enctypes = [] - @density = nil + @step_chances = nil + @encounter_tables = [] end - def setup(mapID) - @stepcount = 0 - @density = nil - @enctypes = [] - begin - data = pbLoadEncountersData - if data.is_a?(Hash) && data[mapID] - @density = data[mapID][0] - @enctypes = data[mapID][1] - else - @density = nil - @enctypes = [] - end - rescue - @density = nil - @enctypes = [] + def setup(map_ID) + @step_count = 0 + @step_chances = nil + @encounter_tables = [] + encounter_data = GameData::Encounter.get(map_ID, $PokemonGlobal.encounter_version) + if encounter_data + @step_chances = encounter_data.step_chances.clone + @encounter_tables = Marshal.load(Marshal.dump(encounter_data.types)) end end - def clearStepCount; @stepcount = 0; end + def reset_step_count + @step_count = 0 + end + + #============================================================================= # Returns whether encounters for the given encounter type have been defined # for the current map. - def hasEncounter?(enc) - return false if @density==nil || enc<0 - return @enctypes[enc] ? true : false + def has_encounter_type?(enc_type) + return false if enc_type < 0 + return @encounter_tables[enc_type] && @encounter_tables[enc_type].length > 0 end # Returns whether encounters for the given encounter type have been defined # for the given map. Only called by Bug Catching Contest to see if it can use # the map's BugContest encounter type to generate caught Pokémon for the other # contestants. - def pbMapHasEncounter?(mapID,enctype) - data = pbLoadEncountersData - if data.is_a?(Hash) && data[mapID] - density = data[mapID][0] - enctypes = data[mapID][1] - else - return false - end - return false if density==nil || enctype<0 - return enctypes[enctype] ? true : false + def map_has_encounter_type?(map_ID, enc_type) + return false if enc_type < 0 + encounter_data = GameData::Encounter.get(map_ID, $PokemonGlobal.encounter_version) + return false if !encounter_data + return encounter_data.types[enc_type] && encounter_data.types[enc_type].length > 0 + end + + # Returns whether land-like encounters have been defined for the current map. + # Applies only to encounters triggered by moving around. + def has_land_encounters? + return has_normal_land_encounters? || + has_encounter_type?(EncounterTypes::BugContest) + end + + # Returns whether land-like encounters have been defined for the current map + # (ignoring the Bug Catching Contest one). + # Applies only to encounters triggered by moving around. + def has_normal_land_encounters? + return has_encounter_type?(EncounterTypes::Land) || + has_encounter_type?(EncounterTypes::LandDay) || + has_encounter_type?(EncounterTypes::LandNight) || + has_encounter_type?(EncounterTypes::LandMorning) || + has_encounter_type?(EncounterTypes::LandAfternoon) || + has_encounter_type?(EncounterTypes::LandEvening) end # Returns whether cave-like encounters have been defined for the current map. # Applies only to encounters triggered by moving around. - def isCave? - return false if @density==nil - return @enctypes[EncounterTypes::Cave] ? true : false - end - - # Returns whether grass-like encounters have been defined for the current map. - # Applies only to encounters triggered by moving around. - def isGrass? - return false if @density==nil - return (@enctypes[EncounterTypes::Land] || - @enctypes[EncounterTypes::LandMorning] || - @enctypes[EncounterTypes::LandDay] || - @enctypes[EncounterTypes::LandNight] || - @enctypes[EncounterTypes::BugContest]) ? true : false - end - - # Returns whether grass-like encounters have been defined for the current map - # (ignoring the Bug Catching Contest one). - # Applies only to encounters triggered by moving around. - def isRegularGrass? - return false if @density==nil - return (@enctypes[EncounterTypes::Land] || - @enctypes[EncounterTypes::LandMorning] || - @enctypes[EncounterTypes::LandDay] || - @enctypes[EncounterTypes::LandNight]) ? true : false + def has_cave_encounters? + return has_encounter_type?(EncounterTypes::Cave) || + has_encounter_type?(EncounterTypes::CaveDay) || + has_encounter_type?(EncounterTypes::CaveNight) || + has_encounter_type?(EncounterTypes::CaveMorning) || + has_encounter_type?(EncounterTypes::CaveAfternoon) || + has_encounter_type?(EncounterTypes::CaveEvening) end # Returns whether water-like encounters have been defined for the current map. # Applies only to encounters triggered by moving around (i.e. not fishing). - def isWater? - return false if @density==nil - return @enctypes[EncounterTypes::Water] ? true : false + def has_water_encounters? + return has_encounter_type?(EncounterTypes::Water) || + has_encounter_type?(EncounterTypes::WaterDay) || + has_encounter_type?(EncounterTypes::WaterNight) || + has_encounter_type?(EncounterTypes::WaterMorning) || + has_encounter_type?(EncounterTypes::WaterAfternoon) || + has_encounter_type?(EncounterTypes::WaterEvening) end - # Returns whether it is theoretically possible to have an encounter in the - # player's current location. - def isEncounterPossibleHere? - if $PokemonGlobal.surfing - return true - elsif PBTerrain.isIce?(pbGetTerrainTag($game_player)) - return false - elsif self.isCave? - return true - elsif self.isGrass? - return PBTerrain.isGrass?($game_map.terrain_tag($game_player.x,$game_player.y)) - end + #============================================================================= + + # Returns whether the player's current location allows wild encounters to + # trigger upon taking a step. + def encounter_possible_here? + return true if $PokemonGlobal.surfing + terrain_tag = $game_map.terrain_tag($game_player.x, $game_player.y) + return false if PBTerrain.isIce?(terrain_tag) + return true if has_cave_encounters? # i.e. this map is a cave + return true if PBTerrain.isGrass?(terrain_tag) && has_land_encounters? return false end - # Returns the encounter method that the current encounter should be generated - # from, depending on the player's current location. - def pbEncounterType - if $PokemonGlobal.surfing - return EncounterTypes::Water - elsif self.isCave? - return EncounterTypes::Cave - elsif self.isGrass? - time = pbGetTimeNow - enctype = EncounterTypes::Land - enctype = EncounterTypes::LandNight if self.hasEncounter?(EncounterTypes::LandNight) && PBDayNight.isNight?(time) - enctype = EncounterTypes::LandDay if self.hasEncounter?(EncounterTypes::LandDay) && PBDayNight.isDay?(time) - enctype = EncounterTypes::LandMorning if self.hasEncounter?(EncounterTypes::LandMorning) && PBDayNight.isMorning?(time) - if pbInBugContest? && self.hasEncounter?(EncounterTypes::BugContest) - enctype = EncounterTypes::BugContest - end - return enctype - end - return -1 - end - - # Returns all the encounter tables for the given map. - # You can alias this method and modify the returned array's contents if you - # want to change the encounter table for some reason. Note that each sub-array - # should contain the right number of entries for that encounter type. - # Each encounter table element is an array: [species, minLevel, maxLevel] - def pbGetEncounterTables(mapID=-1) - if mapID>0 - data = pbLoadEncountersData - return nil if !data.is_a?(Hash) || !data[mapID] - return data[mapID][1] - else # Current map - return Marshal.load(Marshal.dump(@enctypes)) - end - end - - # Returns an array of the encounter table for the given map/encounter type. - def pbGetEncounterTable(encType,mapID=-1) - ret = pbGetEncounterTables(mapID) - return ret[encType] - end - - # Only called by Bug Catching Contest, when determining what the other - # contestants caught. - def pbMapEncounter(mapID,encType) - if encType<0 || encType>EncounterTypes::EnctypeChances.length + # Returns whether a wild encounter should happen, based on the probability of + # one triggering upon taking a step. + def step_triggers_encounter?(enc_type) + if enc_type < 0 || enc_type > EncounterTypes::Probabilities.length raise ArgumentError.new(_INTL("Encounter type out of range")) end - # Get the encounter table - encList = pbGetEncounterTable(encType,mapID) - return nil if encList==nil - # Calculate the total probability value - chances = EncounterTypes::EnctypeChances[encType] - chanceTotal = 0 - chances.each { |a| chanceTotal += a } - # Choose a random entry in the encounter table based on entry weights - rnd = rand(chanceTotal) - chance = 0 - chosenPkmn = 0 # Index of the chosen entry - for i in 0...chances.length - chance += chances[i] - if rndEncounterTypes::EnctypeChances.length - raise ArgumentError.new(_INTL("Encounter type out of range")) - end - # Get the encounter table - encList = pbGetEncounterTable(enctype) - return nil if encList==nil - chances = EncounterTypes::EnctypeChances[enctype] - # Static/Magnet Pull prefer wild encounters of certain types, if possible. - # If they activate, they remove all Pokémon from the encounter table that do - # not have the type they favor. If none have that type, nothing is changed. - firstPkmn = $Trainer.first_pokemon - if firstPkmn && rand(100)<50 # 50% chance of happening - favoredType = nil - if firstPkmn.hasAbility?(:STATIC) && GameData::Type.exists?(:ELECTRIC) - favoredType = :ELECTRIC - elsif firstPkmn.hasAbility?(:MAGNETPULL) && GameData::Type.exists?(:STEEL) - favoredType = :STEEL - end - if favoredType - newEncList = [] - newChances = [] - for i in 0...encList.length - speciesData = GameData::Species.get(encList[i][0]) - t1 = speciesData.type1 - t2 = speciesData.type2 - next if t1 != favoredType && (!t2 || t2 != favoredType) - newEncList.push(encList[i]) - newChances.push(chances[i]) - end - if newEncList.length>0 - encList = newEncList - chances = newChances - end - end - end - # Calculate the total probability value - chanceTotal = 0 - chances.each { |a| chanceTotal += a } - # Choose a random entry in the encounter table based on entry weights - rnd = 0 - tries.times do - r = rand(chanceTotal) - rnd = r if rndlevel # Higher level is more likely - end - end - # Black Flute and White Flute alter the level of the wild Pokémon - if FLUTES_CHANGE_WILD_ENCOUNTER_LEVELS - if $PokemonMap.blackFluteUsed - level = [level + 1 + rand(4), PBExperience.maxLevel].min - elsif $PokemonMap.whiteFluteUsed - level = [level - 1 - rand(4), 1].max - end - end - # Return [species, level] - return [encounter[0],level] - end - - # Returns the encountered Pokémon's species/level, taking into account factors - # that alter the probability of an encounter (cycling, Flutes, lead party - # Pokémon's item/ability). - def pbGenerateEncounter(enctype) - if enctype<0 || enctype>EncounterTypes::EnctypeChances.length - raise ArgumentError.new(_INTL("Encounter type out of range")) - end - # Check if an encounter table is defined - return nil if @density==nil - return nil if @density[enctype]==0 || !@density[enctype] - return nil if @enctypes[enctype]==nil + return false if $game_system.encounter_disabled + return false if !$Trainer + return false if $DEBUG && Input.press?(Input::CTRL) # Wild encounters cannot happen for the first 3 steps after a previous wild # encounter - @stepcount += 1 - return nil if @stepcount<=3 - # Determine the encounter density (probability of a wild encounter - # happening). The actual probability is the written encounter density, with - # modifiers applied, divided by 180 (numbers are multiplied by 16 below to - # increase precision). - encount = @density[enctype]*16 + @step_count += 1 + return false if @step_count <= 3 + # Check if enc_type has a defined step chance/encounter table + return false if !@step_chances[enc_type] || @step_chances[enc_type] == 0 + return false if !has_encounter_type?(enc_type) + # Determine the encounter step chance (probability of a wild encounter + # happening). The actual probability is the written encounter step chance, + # with modifiers applied, divided by 180. + encount = @step_chances[enc_type].to_f encount *= 0.8 if $PokemonGlobal.bicycle if !FLUTES_CHANGE_WILD_ENCOUNTER_LEVELS - if $PokemonMap.blackFluteUsed - encount /= 2 - elsif $PokemonMap.whiteFluteUsed - encount *= 1.5 - end + encount /= 2 if $PokemonMap.blackFluteUsed + encount *= 1.5 if $PokemonMap.whiteFluteUsed end - firstPkmn = $Trainer.first_pokemon - if firstPkmn - case firstPkmn.item_id + first_pkmn = $Trainer.first_pokemon + if first_pkmn + case first_pkmn.item_id when :CLEANSETAG encount *= 2.0 / 3 when :PUREINCENSE encount *= 2.0 / 3 else # Ignore ability effects if an item effect applies - case firstPkmn.ability_id + case first_pkmn.ability_id when :STENCH, :WHITESMOKE, :QUICKFEET encount /= 2 when :SNOWCLOAK - encount /= 2 if $game_screen.weather_type==PBFieldWeather::Snow || - $game_screen.weather_type==PBFieldWeather::Blizzard + encount /= 2 if $game_screen.weather_type == PBFieldWeather::Snow || + $game_screen.weather_type == PBFieldWeather::Blizzard when :SANDVEIL - encount /= 2 if $game_screen.weather_type==PBFieldWeather::Sandstorm + encount /= 2 if $game_screen.weather_type == PBFieldWeather::Sandstorm when :SWARM encount *= 1.5 when :ILLUMINATE, :ARENATRAP, :NOGUARD @@ -360,34 +244,206 @@ class PokemonEncounters end end # Decide whether the wild encounter should actually happen - return nil if rand(180*16)>=encount - # A wild encounter will happen; choose a species and level for it - encPkmn = pbEncounteredPokemon(enctype) - return nil if !encPkmn - # Some abilities make wild encounters less likely if the wild Pokémon is - # sufficiently weaker than the Pokémon with the ability - if firstPkmn && rand(100)<50 # 50% chance of happening - if firstPkmn.hasAbility?(:INTIMIDATE) || firstPkmn.hasAbility?(:KEENEYE) - return nil if encPkmn[1]<=firstPkmn.level-5 # 5 or more levels weaker - end - end - return encPkmn + return rand(180) < encount end - # Returns whether it is possible to have an encounter, based on some factors - # that would prevent it (holding Ctrl in Debug mode, Repels). - def pbCanEncounter?(encounter,repel) - return false if $game_system.encounter_disabled - return false if !encounter || !$Trainer - return false if $DEBUG && Input.press?(Input::CTRL) - if !pbPokeRadarOnShakingGrass - if $PokemonGlobal.repel>0 || repel - firstPkmn = (REPEL_COUNTS_FAINTED_POKEMON) ? $Trainer.first_pokemon : $Trainer.first_able_pokemon - return false if firstPkmn && encounter[1] 0 && !pbPokeRadarOnShakingGrass + first_pkmn = (REPEL_COUNTS_FAINTED_POKEMON) ? $Trainer.first_pokemon : $Trainer.first_able_pokemon + return false if first_pkmn && enc_data[1] < first_pkmn.level + end + # Some abilities make wild encounters less likely if the wild Pokémon is + # sufficiently weaker than the Pokémon with the ability + first_pkmn = $Trainer.first_pokemon + if first_pkmn + case first_pkmn.ability_id + when :INTIMIDATE, :KEENEYE + return false if enc_data[1] <= first_pkmn.level - 5 && rand(100) < 50 end end return true end + + # Returns whether a wild encounter should be turned into a double wild + # encounter. + def have_double_wild_battle? + return false if $PokemonTemp.forceSingleBattle + return false if pbInSafari? + return true if $PokemonGlobal.partner + return false if $Trainer.able_pokemon_count <= 1 + return true if PBTerrain.isDoubleWildBattle?(pbGetTerrainTag) && rand(100) < 30 + return false + end + + # Returns the encounter method that the current encounter should be generated + # from, depending on the player's current location. + def encounter_type + time = pbGetTimeNow + ret = -1 + if $PokemonGlobal.surfing + ret = EncounterTypes::Water if has_encounter_type?(EncounterTypes::Water) + if PBDayNight.isDay?(time) + ret = EncounterTypes::WaterDay if has_encounter_type?(EncounterTypes::WaterDay) + if PBDayNight.isMorning?(time) + ret = EncounterTypes::WaterMorning if has_encounter_type?(EncounterTypes::WaterMorning) + elsif PBDayNight.isAfternoon?(time) + ret = EncounterTypes::WaterAfternoon if has_encounter_type?(EncounterTypes::WaterAfternoon) + elsif PBDayNight.isEvening?(time) + ret = EncounterTypes::WaterEvening if has_encounter_type?(EncounterTypes::WaterEvening) + end + else + ret = EncounterTypes::WaterNight if has_encounter_type?(EncounterTypes::WaterNight) + end + else + check_land = false + if has_cave_encounters? + check_land = PBTerrain.isGrass?($game_map.terrain_tag($game_player.x, $game_player.y)) + ret = EncounterTypes::Cave if has_encounter_type?(EncounterTypes::Cave) + if PBDayNight.isDay?(time) + ret = EncounterTypes::CaveDay if has_encounter_type?(EncounterTypes::CaveDay) + if PBDayNight.isMorning?(time) + ret = EncounterTypes::CaveMorning if has_encounter_type?(EncounterTypes::CaveMorning) + elsif PBDayNight.isAfternoon?(time) + ret = EncounterTypes::CaveAfternoon if has_encounter_type?(EncounterTypes::CaveAfternoon) + elsif PBDayNight.isEvening?(time) + ret = EncounterTypes::CaveEvening if has_encounter_type?(EncounterTypes::CaveEvening) + end + else + ret = EncounterTypes::CaveNight if has_encounter_type?(EncounterTypes::CaveNight) + end + end + # Land + if has_land_encounters? || check_land + ret = EncounterTypes::Land if has_encounter_type?(EncounterTypes::Land) + if PBDayNight.isDay?(time) + ret = EncounterTypes::LandDay if has_encounter_type?(EncounterTypes::LandDay) + if PBDayNight.isMorning?(time) + ret = EncounterTypes::LandMorning if has_encounter_type?(EncounterTypes::LandMorning) + elsif PBDayNight.isAfternoon?(time) + ret = EncounterTypes::LandAfternoon if has_encounter_type?(EncounterTypes::LandAfternoon) + elsif PBDayNight.isEvening?(time) + ret = EncounterTypes::LandEvening if has_encounter_type?(EncounterTypes::LandEvening) + end + else + ret = EncounterTypes::LandNight if has_encounter_type?(EncounterTypes::LandNight) + end + if pbInBugContest? && has_encounter_type?(EncounterTypes::BugContest) + ret = EncounterTypes::BugContest + end + end + end + return ret + end + + #============================================================================= + + # For the current map, randomly chooses a species and level from the encounter + # list for the given encounter type. Returns nil if there are none defined. + # A higher chance_rolls makes this method prefer rarer encounter slots. + def choose_wild_pokemon(enc_type, chance_rolls = 1) + if enc_type < 0 || enc_type > EncounterTypes::Probabilities.length + raise ArgumentError.new(_INTL("Encounter type out of range")) + end + enc_list = @encounter_tables[enc_type] + return nil if !enc_list || enc_list.length == 0 + # Static/Magnet Pull prefer wild encounters of certain types, if possible. + # If they activate, they remove all Pokémon from the encounter table that do + # not have the type they favor. If none have that type, nothing is changed. + first_pkmn = $Trainer.first_pokemon + if first_pkmn + favored_type = nil + case first_pkmn.ability_id + when :STATIC + favored_type = :ELECTRIC if GameData::Type.exists?(:ELECTRIC) && rand(100) < 50 + when :MAGNETPULL + favored_type = :STEEL if GameData::Type.exists?(:STEEL) && rand(100) < 50 + end + if favored_type + new_enc_list = [] + enc_list.each do |enc| + species_data = GameData::Species.get(enc[0]) + t1 = species_data.type1 + t2 = species_data.type2 + new_enc_list.push(enc) if t1 == favored_type || t2 == favored_type + end + enc_list = new_enc_list if new_enc_list.length > 0 + end + end + enc_list.sort! { |a, b| b[0] <=> a[0] } # Highest probability first + # Calculate the total probability value + chance_total = 0 + enc_list.each { |a| chance_total += a[0] } + # Choose a random entry in the encounter table based on entry probabilities + rnd = 0 + chance_rolls.times do + r = rand(chance_total) + rnd = r if r > rnd # Prefer rarer entries if rolling repeatedly + end + encounter = nil + enc_list.each do |enc| + rnd -= enc[0] + next if rnd >= 0 + encounter = enc + break + end + # Get the chosen species and level + level = rand(encounter[2], encounter[3]) + # Some abilities alter the level of the wild Pokémon + if first_pkmn + case first_pkmn.ability_id + when :HUSTLE, :PRESSURE, :VITALSPIRIT + level = encounter[3] if rand(100) < 50 # Highest possible level + end + end + # Black Flute and White Flute alter the level of the wild Pokémon + if FLUTES_CHANGE_WILD_ENCOUNTER_LEVELS + if $PokemonMap.blackFluteUsed + level = [level + rand(1, 4), PBExperience.maxLevel].min + elsif $PokemonMap.whiteFluteUsed + level = [level - rand(1, 4), 1].max + end + end + # Return [species, level] + return [encounter[1], level] + end + + # For the given map, randomly chooses a species and level from the encounter + # list for the given encounter type. Returns nil if there are none defined. + # Used by the Bug Catching Contest to choose what the other participants + # caught. + def choose_wild_pokemon_for_map(map_ID, enc_type) + if enc_type < 0 || enc_type > EncounterTypes::Probabilities.length + raise ArgumentError.new(_INTL("Encounter type out of range")) + end + # Get the encounter table + encounter_data = GameData::Encounter.get(map_ID, $PokemonGlobal.encounter_version) + return nil if !encounter_data + enc_list = encounter_data.types[enc_type] + return nil if !enc_list || enc_list.length == 0 + # Calculate the total probability value + chance_total = 0 + enc_list.each { |a| chance_total += a[0] } + # Choose a random entry in the encounter table based on entry probabilities + rnd = 0 + chance_rolls.times do + r = rand(chance_total) + rnd = r if r > rnd # Prefer rarer entries if rolling repeatedly + end + encounter = nil + enc_list.each do |enc| + rnd -= enc[0] + next if rnd >= 0 + encounter = enc + break + end + # Return [species, level] + level = rand(encounter[2], encounter[3]) + return [encounter[1], level] + end end @@ -395,14 +451,16 @@ end #=============================================================================== # #=============================================================================== -# Returns a Pokémon generated by a wild encounter, given its species and level. +# Creates and returns a Pokémon based on the given species and level. +# Applies wild Pokémon modifiers (wild held item, shiny chance modifiers, +# Pokérus, gender/nature forcing because of player's lead Pokémon). def pbGenerateWildPokemon(species,level,isRoamer=false) genwildpoke = Pokemon.new(species,level) # Give the wild Pokémon a held item items = genwildpoke.wildHoldItems - firstPkmn = $Trainer.first_pokemon + first_pkmn = $Trainer.first_pokemon chances = [50,5,1] - chances = [60,20,5] if firstPkmn && firstPkmn.hasAbility?(:COMPOUNDEYES) + chances = [60,20,5] if first_pkmn && first_pkmn.hasAbility?(:COMPOUNDEYES) itemrnd = rand(100) if (items[0]==items[1] && items[1]==items[2]) || itemrnd0 && !$game_switches[roamData[2]] # Game Switch is off - next if $PokemonGlobal.roamPokemon[i]==true # Roaming Pokémon has been caught - next if !GameData::Species.exists?(roamData[0]) - # Get the roaming Pokémon's current map + currentRegion = pbGetCurrentRegion + currentMapName = pbGetMessage(MessageTypes::MapNames, $game_map.map_id) + possible_roamers = [] + ROAMING_SPECIES.each_with_index do |data, i| + # data = [species, level, Game Switch, roamer method, battle BGM, area maps hash] + next if !GameData::Species.exists?(data[0]) + next if data[2] > 0 && !$game_switches[data[2]] # Isn't roaming + next if $PokemonGlobal.roamPokemon[i] == true # Roaming Pokémon has been caught + # Get the roamer's current map roamerMap = $PokemonGlobal.roamPosition[i] if !roamerMap mapIDs = pbRoamingAreas(i).keys # Hash of area patrolled by the roaming Pokémon - next if !mapIDs || mapIDs.length==0 # No roaming area defined somehow + next if !mapIDs || mapIDs.length == 0 # No roaming area defined somehow roamerMap = mapIDs[rand(mapIDs.length)] $PokemonGlobal.roamPosition[i] = roamerMap end - # Check if roaming Pokémon is on the current map. If not, check if roaming - # Pokémon is on a map with the same name as the current map and both maps - # are in the same region - if roamerMap!=$game_map.map_id - currentRegion = pbGetCurrentRegion + # If roamer isn't on the current map, check if it's on a map with the same + # name and in the same region + if roamerMap != $game_map.map_id map_metadata = GameData::MapMetadata.try_get(roamerMap) next if !map_metadata || !map_metadata.town_map_position || map_metadata.town_map_position[0] != currentRegion - currentMapName = pbGetMessage(MessageTypes::MapNames,$game_map.map_id) - next if pbGetMessage(MessageTypes::MapNames,roamerMap)!=currentMapName + next if pbGetMessage(MessageTypes::MapNames, roamerMap) != currentMapName end - # Check whether the roaming Pokémon's category of encounter is currently possible - next if !pbRoamingMethodAllowed(roamData[3]) + # Check whether the roamer's roamer method is currently possible + next if !pbRoamingMethodAllowed(data[3]) # Add this roaming Pokémon to the list of possible roaming Pokémon to encounter - roamerChoices.push([i,roamData[0],roamData[1],roamData[4]]) + possible_roamers.push([i, data[0], data[1], data[4]]) # [i, species, level, BGM] end # No encounterable roaming Pokémon were found, just have the regular encounter - next encounter if roamerChoices.length==0 + next encounter if possible_roamers.length == 0 # Pick a roaming Pokémon to encounter out of those available - chosenRoamer = roamerChoices[rand(roamerChoices.length)] - $PokemonGlobal.roamEncounter = chosenRoamer - $PokemonTemp.roamerIndex = chosenRoamer[0] # Roaming Pokémon's index - if chosenRoamer[3] && chosenRoamer[3]!="" - $PokemonGlobal.nextBattleBGM = chosenRoamer[3] - end + roamer = possible_roamers[rand(possible_roamers.length)] + $PokemonGlobal.roamEncounter = roamer + $PokemonTemp.roamerIndex = roamer[0] + $PokemonGlobal.nextBattleBGM = roamer[3] if roamer[3] && !roamer[3].empty? $PokemonTemp.forceSingleBattle = true - next [chosenRoamer[1],chosenRoamer[2]] # Species, level + next [roamer[1], roamer[2]] # Species, level }) Events.onWildBattleOverride += proc { |_sender,e| @@ -207,9 +193,8 @@ Events.onWildBattleOverride += proc { |_sender,e| level = e[1] handled = e[2] next if handled[0]!=nil - next if !$PokemonGlobal.roamEncounter - next if $PokemonTemp.roamerIndex==nil - handled[0] = pbRoamingPokemonBattle(species,level) + next if !$PokemonGlobal.roamEncounter || $PokemonTemp.roamerIndex.nil? + handled[0] = pbRoamingPokemonBattle(species, level) } def pbRoamingPokemonBattle(species, level) diff --git a/Data/Scripts/013_Overworld/011_PField_FieldMoves.rb b/Data/Scripts/013_Overworld/011_PField_FieldMoves.rb index f3348f1cc..9d2a46294 100644 --- a/Data/Scripts/013_Overworld/011_PField_FieldMoves.rb +++ b/Data/Scripts/013_Overworld/011_PField_FieldMoves.rb @@ -714,7 +714,7 @@ end def pbStartSurfing pbCancelVehicles - $PokemonEncounters.clearStepCount + $PokemonEncounters.reset_step_count $PokemonGlobal.surfing = true pbUpdateVehicle $PokemonTemp.surfJump = $MapFactory.getFacingCoords($game_player.x,$game_player.y,$game_player.direction) @@ -831,8 +831,8 @@ def pbSweetScent break if viewport.color.alpha<=0 end viewport.dispose - enctype = $PokemonEncounters.pbEncounterType - if enctype<0 || !$PokemonEncounters.isEncounterPossibleHere? || + enctype = $PokemonEncounters.encounter_type + if enctype < 0 || !$PokemonEncounters.encounter_possible_here? || !pbEncounter(enctype) pbMessage(_INTL("There appears to be nothing here...")) end diff --git a/Data/Scripts/015_Items/002_PItem_ItemEffects.rb b/Data/Scripts/015_Items/002_PItem_ItemEffects.rb index f90d6074c..87894e1f4 100644 --- a/Data/Scripts/015_Items/002_PItem_ItemEffects.rb +++ b/Data/Scripts/015_Items/002_PItem_ItemEffects.rb @@ -242,7 +242,7 @@ ItemHandlers::UseInField.add(:OLDROD,proc { |item| pbMessage(_INTL("Can't use that here.")) next 0 end - encounter = $PokemonEncounters.hasEncounter?(EncounterTypes::OldRod) + encounter = $PokemonEncounters.has_encounter_type?(EncounterTypes::OldRod) if pbFishing(encounter,1) pbEncounter(EncounterTypes::OldRod) end @@ -256,7 +256,7 @@ ItemHandlers::UseInField.add(:GOODROD,proc { |item| pbMessage(_INTL("Can't use that here.")) next 0 end - encounter = $PokemonEncounters.hasEncounter?(EncounterTypes::GoodRod) + encounter = $PokemonEncounters.has_encounter_type?(EncounterTypes::GoodRod) if pbFishing(encounter,2) pbEncounter(EncounterTypes::GoodRod) end @@ -270,7 +270,7 @@ ItemHandlers::UseInField.add(:SUPERROD,proc { |item| pbMessage(_INTL("Can't use that here.")) next 0 end - encounter = $PokemonEncounters.hasEncounter?(EncounterTypes::SuperRod) + encounter = $PokemonEncounters.has_encounter_type?(EncounterTypes::SuperRod) if pbFishing(encounter,3) pbEncounter(EncounterTypes::SuperRod) end diff --git a/Data/Scripts/015_Items/004_PItem_Phone.rb b/Data/Scripts/015_Items/004_PItem_Phone.rb index 19de09285..54a977456 100644 --- a/Data/Scripts/015_Items/004_PItem_Phone.rb +++ b/Data/Scripts/015_Items/004_PItem_Phone.rb @@ -243,27 +243,34 @@ def pbRandomPhoneItem(array) return pbGetMessageFromHash(MessageTypes::PhoneMessages,ret) end -def pbRandomEncounterSpecies(enctype) - return 0 if !enctype - len = [enctype.length,4].min - return enctype[rand(len)][0] +def pbRandomEncounterSpecies(enc_table) + return nil if !enc_table || enc_table.length == 0 + len = [enc_table.length, 4].min + return enc_table[rand(len)][1] end def pbEncounterSpecies(phonenum) - return "" if !phonenum[6] || phonenum[6]==0 - begin - enctypes = $PokemonEncounters.pbGetEncounterTables(phonenum[6]) - return "" if !enctypes - rescue - return "" + return "" if !phonenum[6] || phonenum[6] == 0 + encounter_data = GameData::Encounter.get(phonenum[6], $PokemonGlobal.encounter_version) + return "" if !encounter_data + enc_tables = encounter_data.types + species = pbRandomEncounterSpecies(enc_tables[EncounterTypes::Land]) + if !species + species = pbRandomEncounterSpecies(enc_tables[EncounterTypes::Cave]) + if !species + species = pbRandomEncounterSpecies(enc_tables[EncounterTypes::LandDay]) + if !species + species = pbRandomEncounterSpecies(enc_tables[EncounterTypes::LandMorning]) + if !species + species = pbRandomEncounterSpecies(enc_tables[EncounterTypes::LandNight]) + if !species + species = pbRandomEncounterSpecies(enc_tables[EncounterTypes::Water]) + end + end + end + end end - species = pbRandomEncounterSpecies(enctypes[EncounterTypes::Land]) - species = pbRandomEncounterSpecies(enctypes[EncounterTypes::Cave]) if species==0 - species = pbRandomEncounterSpecies(enctypes[EncounterTypes::LandDay]) if species==0 - species = pbRandomEncounterSpecies(enctypes[EncounterTypes::LandMorning]) if species==0 - species = pbRandomEncounterSpecies(enctypes[EncounterTypes::LandNight]) if species==0 - species = pbRandomEncounterSpecies(enctypes[EncounterTypes::Water]) if species==0 - return "" if species==0 + return "" if !species return GameData::Species.get(species).name end diff --git a/Data/Scripts/015_Items/005_PItem_PokeRadar.rb b/Data/Scripts/015_Items/005_PItem_PokeRadar.rb index 225fe553b..dd5fde99e 100644 --- a/Data/Scripts/015_Items/005_PItem_PokeRadar.rb +++ b/Data/Scripts/015_Items/005_PItem_PokeRadar.rb @@ -20,7 +20,7 @@ def pbCanUsePokeRadar? return false end # Can't use Radar if map has no grass-based encounters (ignoring Bug Contest) - if !$PokemonEncounters.isRegularGrass? + if !$PokemonEncounters.has_normal_land_encounters? pbMessage(_INTL("Can't use that here.")) return false end @@ -48,6 +48,10 @@ def pbUsePokeRadar return true end +def pbPokeRadarCancel + $PokemonTemp.pokeradar = nil +end + def pbPokeRadarHighlightGrass(showmessage=true) grasses = [] # x, y, ring (0-3 inner to outer), rarity # Choose 1 random tile from each ring around the player @@ -71,7 +75,7 @@ def pbPokeRadarHighlightGrass(showmessage=true) y>=0 && y<$game_map.height if PBTerrain.isJustGrass?($game_map.terrain_tag(x,y)) # Choose a rarity for the grass (0=normal, 1=rare, 2=shiny) - s = (rand(4)==0) ? 1 : 0 + s = (rand(100) < 25) ? 1 : 0 if $PokemonTemp.pokeradar && $PokemonTemp.pokeradar[2]>0 v = [(65536/SHINY_POKEMON_CHANCE)-$PokemonTemp.pokeradar[2]*200,200].max v = 0xFFFF/v @@ -103,88 +107,79 @@ def pbPokeRadarHighlightGrass(showmessage=true) end end -def pbPokeRadarCancel - $PokemonTemp.pokeradar = nil -end - def pbPokeRadarGetShakingGrass return -1 if !$PokemonTemp.pokeradar grasses = $PokemonTemp.pokeradar[3] - return -1 if grasses.length==0 + return -1 if grasses.length == 0 for i in grasses - return i[2] if $game_player.x==i[0] && $game_player.y==i[1] + return i[2] if $game_player.x == i[0] && $game_player.y == i[1] end return -1 end def pbPokeRadarOnShakingGrass - return pbPokeRadarGetShakingGrass>=0 + return pbPokeRadarGetShakingGrass >= 0 end -def pbPokeRadarGetEncounter(rarity=0) +def pbPokeRadarGetEncounter(rarity = 0) # Poké Radar-exclusive encounters can only be found in vigorously-shaking grass - if rarity>0 + if rarity > 0 # Get all Poké Radar-exclusive encounters for this map - map = $game_map.map_id rescue 0 + map = $game_map.map_id array = [] - for enc in POKE_RADAR_ENCOUNTERS - array.push(enc) if enc.length>=4 && enc[0]==map && GameData::Species.exists?(enc[2]) + POKE_RADAR_ENCOUNTERS.each do |enc| + array.push(enc) if enc[0] == map && GameData::Species.exists?(enc[2]) end # If there are any exclusives, first have a chance of encountering those - if array.length>0 + if array.length > 0 rnd = rand(100) chance = 0 - for enc in array - chance += enc[1] - if rnd enc[3]) ? rand(enc[3], enc[4]) : enc[3] - return [enc[2], level] - end + array.each do |enc| + rnd -= enc[1] + next if rnd >= 0 + level = (enc[4] && enc[4] > enc[3]) ? rand(enc[3], enc[4]) : enc[3] + return [enc[2], level] end end end # Didn't choose a Poké Radar-exclusive species, choose a regular encounter instead - return $PokemonEncounters.pbEncounteredPokemon($PokemonEncounters.pbEncounterType,rarity+1) + return $PokemonEncounters.choose_wild_pokemon($PokemonEncounters.encounter_type, rarity + 1) end ################################################################################ # Event handlers ################################################################################ EncounterModifier.register(proc { |encounter| - next encounter if EncounterTypes::EnctypeCompileDens[$PokemonTemp.encounterType]!=1 - if !$PokemonEncounters.isRegularGrass? || - !$PokemonEncounters.isEncounterPossibleHere? || - $PokemonGlobal.partner + if !EncounterTypes.is_normal_land_type?($PokemonTemp.encounterType) || + $PokemonGlobal.bicycle || $PokemonGlobal.partner pbPokeRadarCancel next encounter end - grass = pbPokeRadarGetShakingGrass - if grass>=0 + ring = pbPokeRadarGetShakingGrass + if ring >= 0 # Encounter triggered by stepping into rustling grass # Get rarity of shaking grass - s = 0 - for g in $PokemonTemp.pokeradar[3] - s = g[3] if g[2]==grass - end - if $PokemonTemp.pokeradar[2]>0 - if s==2 || rand(100)<86+grass*4+($PokemonTemp.pokeradar[2]/4).floor + rarity = 0 # 0 = rustle, 1 = vigorous rustle, 2 = shiny rustle + $PokemonTemp.pokeradar[3].each { |g| rarity = g[3] if g[2] == ring } + if $PokemonTemp.pokeradar[2] > 0 # Chain count, i.e. is chaining + if rarity == 2 || rand(100) < 86 + ring * 4 + ($PokemonTemp.pokeradar[2] / 4).floor # Continue the chain - encounter = [$PokemonTemp.pokeradar[0],$PokemonTemp.pokeradar[1]] - $PokemonTemp.forceSingleBattle = true + encounter = [$PokemonTemp.pokeradar[0], $PokemonTemp.pokeradar[1]] + $PokemonTemp.forceSingleBattle = true else # Break the chain, force an encounter with a different species 100.times do - break if encounter && encounter[0]!=$PokemonTemp.pokeradar[0] - encounter = $PokemonEncounters.pbEncounteredPokemon($PokemonEncounters.pbEncounterType) + break if encounter && encounter[0] != $PokemonTemp.pokeradar[0] + encounter = $PokemonEncounters.choose_wild_pokemon($PokemonEncounters.encounter_type) end pbPokeRadarCancel end - else + else # Not chaining; will start one # Force random wild encounter, vigorous shaking means rarer species - encounter = pbPokeRadarGetEncounter(s) + encounter = pbPokeRadarGetEncounter(rarity) $PokemonTemp.forceSingleBattle = true end - else - pbPokeRadarCancel if encounter # Encounter is not in shaking grass + else # Encounter triggered by stepping in non-rustling grass + pbPokeRadarCancel if encounter end next encounter }) @@ -205,10 +200,6 @@ Events.onWildBattleEnd += proc { |_sender,e| species = e[0] level = e[1] decision = e[2] - if !$PokemonEncounters.isRegularGrass? || $PokemonGlobal.bicycle - pbPokeRadarCancel - next - end if $PokemonTemp.pokeradar && (decision==1 || decision==4) # Defeated/caught $PokemonTemp.pokeradar[0] = species $PokemonTemp.pokeradar[1] = level @@ -221,20 +212,15 @@ Events.onWildBattleEnd += proc { |_sender,e| } Events.onStepTaken += proc { |_sender,_e| - if $PokemonGlobal.pokeradarBattery && $PokemonGlobal.pokeradarBattery>0 && + if $PokemonGlobal.pokeradarBattery && $PokemonGlobal.pokeradarBattery > 0 && !$PokemonTemp.pokeradar $PokemonGlobal.pokeradarBattery -= 1 end - if !$PokemonEncounters.isRegularGrass? || - !PBTerrain.isJustGrass?($game_map.terrain_tag($game_player.x,$game_player.y)) + if !PBTerrain.isJustGrass?($game_map.terrain_tag($game_player.x, $game_player.y)) pbPokeRadarCancel end } -Events.onMapUpdate += proc { |_sender,_e| - pbPokeRadarCancel if $PokemonGlobal.bicycle -} - Events.onMapChange += proc { |_sender,_e| pbPokeRadarCancel } diff --git a/Data/Scripts/017_UI/004_PScreen_PokedexEntry.rb b/Data/Scripts/017_UI/004_PScreen_PokedexEntry.rb index 253a1bdaf..90fa659df 100644 --- a/Data/Scripts/017_UI/004_PScreen_PokedexEntry.rb +++ b/Data/Scripts/017_UI/004_PScreen_PokedexEntry.rb @@ -277,12 +277,12 @@ class PokemonPokedexInfo_Scene pbDrawImagePositions(overlay, imagepos) end - def pbFindEncounter(encounter,species) - return false if !encounter - for i in 0...encounter.length - next if !encounter[i] - for j in 0...encounter[i].length - return true if encounter[i][j][0]==species + def pbFindEncounter(enc_types, species) + return false if !enc_types + enc_types.each do |enc_type| + next if !enc_type + enc_type.each do |slot| + return true if GameData::Species.get(slot[1]).species == species end end return false @@ -298,11 +298,9 @@ class PokemonPokedexInfo_Scene # species can be found points = [] mapwidth = 1+PokemonRegionMap_Scene::RIGHT-PokemonRegionMap_Scene::LEFT - encdata = pbLoadEncountersData - for enc in encdata.keys - enctypes = encdata[enc][1] - next if !pbFindEncounter(enctypes, @species) - map_metadata = GameData::MapMetadata.try_get(enc) + GameData::Encounter.each_of_version($PokemonGlobal.encounter_version) do |enc_data| + next if !pbFindEncounter(enc_data.types, @species) + map_metadata = GameData::MapMetadata.try_get(enc_data.id) mappos = (map_metadata) ? map_metadata.town_map_position : nil next if !mappos || mappos[0] != @region showpoint = true diff --git a/Data/Scripts/019_Other battles/002_PBattle_BugContest.rb b/Data/Scripts/019_Other battles/002_PBattle_BugContest.rb index 96bd71ebe..703c467c9 100644 --- a/Data/Scripts/019_Other battles/002_PBattle_BugContest.rb +++ b/Data/Scripts/019_Other battles/002_PBattle_BugContest.rb @@ -106,16 +106,16 @@ class BugContestState end end enctype=EncounterTypes::BugContest - if !$PokemonEncounters.pbMapHasEncounter?(@contestMap,enctype) + if !$PokemonEncounters.map_has_encounter_type?(@contestMap, enctype) enctype=EncounterTypes::Land end for cont in @contestants - enc=$PokemonEncounters.pbMapEncounter(@contestMap,enctype) + enc=$PokemonEncounters.choose_wild_pokemon_for_map(@contestMap,enctype) if !enc raise _INTL("No encounters for map {1}, so can't judge contest",@contestMap) end pokemon=Pokemon.new(enc[0],enc[1]) - pokemon.hp=1+rand(pokemon.totalhp-1) + pokemon.hp = rand(1, pokemon.totalhp - 1) score=pbBugContestScore(pokemon) judgearray.push([cont,pokemon.species,score]) end diff --git a/Data/Scripts/021_Debug/001_Debug menus/002_Debug_MenuCommands.rb b/Data/Scripts/021_Debug/001_Debug menus/002_Debug_MenuCommands.rb index c4e2c0eb1..479b1d623 100644 --- a/Data/Scripts/021_Debug/001_Debug menus/002_Debug_MenuCommands.rb +++ b/Data/Scripts/021_Debug/001_Debug menus/002_Debug_MenuCommands.rb @@ -887,16 +887,7 @@ DebugMenuCommands.register("setencounters", { "description" => _INTL("Edit the wild Pokémon that can be found on maps, and how they are encountered."), "always_show" => true, "effect" => proc { - encdata = pbLoadEncountersData - map = pbDefaultMap - loop do - map = pbListScreen(_INTL("SET ENCOUNTERS"), MapLister.new(map)) - break if map <= 0 - pbEncounterEditorMap(encdata, map) - end - save_data(encdata, "Data/encounters.dat") - $PokemonTemp.encountersData = nil - Compiler.write_encounters # Rewrite PBS file encounters.txt + pbFadeOutIn { pbEncountersEditor } } }) diff --git a/Data/Scripts/021_Debug/004_Editor_Screens.rb b/Data/Scripts/021_Debug/004_Editor_Screens.rb index 7a9f62a78..ba1a8c806 100644 --- a/Data/Scripts/021_Debug/004_Editor_Screens.rb +++ b/Data/Scripts/021_Debug/004_Editor_Screens.rb @@ -1,217 +1,334 @@ #=============================================================================== # Wild encounters editor #=============================================================================== -def pbEncounterEditorTypes(enc,enccmd) +# Main editor method for editing wild encounters. Lists all defined encounter +# sets, and edits them. +def pbEncountersEditor + map_infos = load_data("Data/MapInfos.rxdata") commands = [] - indexes = [] - haveblank = false - if enc - commands.push(_INTL("Density: {1},{2},{3}", - enc[0][EncounterTypes::Land], - enc[0][EncounterTypes::Cave], - enc[0][EncounterTypes::Water])) - indexes.push(-2) - for i in 0...EncounterTypes::EnctypeChances.length - if enc[1][i] - commands.push(EncounterTypes::Names[i]) - indexes.push(i) - else - haveblank = true - end - end - else - commands.push(_INTL("Density: Not Defined Yet")) - indexes.push(-2) - haveblank = true - end - if haveblank - commands.push(_INTL("[New Encounter Type]")) - indexes.push(-3) - end - enccmd.x = 0 - enccmd.y = 0 - enccmd.height = Graphics.height if enccmd.height>Graphics.height - enccmd.z = 99999 - enccmd.commands = commands - enccmd.active = true - enccmd.index = 0 - enccmd.visible = true - command = 0 + maps = [] + list = pbListWindow([]) + help_window = Window_UnformattedTextPokemon.newWithSize(_INTL("Edit wild encounters"), + Graphics.width / 2, 0, Graphics.width / 2, 96) + help_window.z = 99999 + ret = 0 + need_refresh = true loop do - Graphics.update - Input.update - enccmd.update - if Input.trigger?(Input::A) && indexes[enccmd.index]>=0 - if pbConfirmMessage(_INTL("Delete the encounter type {1}?",commands[enccmd.index])) - enc[1][indexes[enccmd.index]] = nil - commands.delete_at(enccmd.index) - indexes.delete_at(enccmd.index) - enccmd.commands = commands - if enccmd.index>=enccmd.commands.length - enccmd.index = enccmd.commands.length + if need_refresh + commands.clear + maps.clear + commands.push(_INTL("[Add new encounter set]")) + GameData::Encounter.each do |enc_data| + name = (map_infos[enc_data.map]) ? map_infos[enc_data.map].name : nil + if enc_data.version > 0 && name + commands.push(sprintf("%03d (v.%d): %s", enc_data.map, enc_data.version, name)) + elsif enc_data.version > 0 + commands.push(sprintf("%03d (v.%d)", enc_data.map, enc_data.version)) + elsif name + commands.push(sprintf("%03d: %s", enc_data.map, name)) + else + commands.push(sprintf("%03d", enc_data.map)) + end + maps.push([enc_data.map, enc_data.version]) + end + need_refresh = false + end + ret = pbCommands2(list, commands, -1, ret) + if ret == 0 # Add new encounter set + new_map_ID = pbListScreen(_INTL("Choose a map"), MapLister.new(pbDefaultMap)) + if new_map_ID > 0 + new_version = LimitProperty2.new(999).set(_INTL("version number"), 0) + if new_version && new_version >= 0 + if GameData::Encounter.exists?(new_map_ID, new_version) + pbMessage(_INTL("A set of encounters for map {1} version {2} already exists.", new_map_ID, new_version)) + else + # Construct encounter hash + key = sprintf("%s_%d", new_map_ID, new_version).to_sym + encounter_hash = { + :id => key, + :map => new_map_ID, + :version => new_version, + :step_chances => [], + :types => [] + } + GameData::Encounter::DATA[encounter_hash[:id]] = GameData::Encounter.new(encounter_hash) + maps.push([new_map_ID, new_version]) + maps.sort! { |a, b| (a[0] == b[0]) ? a[1] <=> b[1] : a[0] <=> b[0] } + ret = maps.index([new_map_ID, new_version]) + 1 + need_refresh = true + end end end - elsif Input.trigger?(Input::B) - command = -1 - break - elsif Input.trigger?(Input::C) || (enccmd.doubleclick? rescue false) - command = enccmd.index - break - end - end - ret = command - enccmd.active = false - return (ret<0) ? -1 : indexes[ret] -end - -def pbNewEncounterType(enc) - cmdwin = pbListWindow([]) - commands =[] - indexes = [] - for i in 0...EncounterTypes::EnctypeChances.length - dogen = false - if !enc[1][i] - if i==0 - dogen = true unless enc[1][EncounterTypes::Cave] - elsif i==1 - dogen = true unless enc[1][EncounterTypes::Land] || - enc[1][EncounterTypes::LandMorning] || - enc[1][EncounterTypes::LandDay] || - enc[1][EncounterTypes::LandNight] || - enc[1][EncounterTypes::BugContest] - else - dogen = true - end - end - if dogen - commands.push(EncounterTypes::Names[i]) - indexes.push(i) - end - end - ret = pbCommands2(cmdwin,commands,-1) - ret = (ret<0) ? -1 : indexes[ret] - if ret>=0 - chances = EncounterTypes::EnctypeChances[ret] - enc[1][ret] = [] - chances.length.times do - enc[1][ret].push([1,5,5]) - end - end - cmdwin.dispose - return ret -end - -def pbEditEncounterType(enc,etype) - commands = [] - cmdwin = pbListWindow([]) - chances = EncounterTypes::EnctypeChances[etype] - chancetotal = 0 - chances.each { |a| chancetotal += a } - enctype = enc[1][etype] - for i in 0...chances.length - enctype[i] = [1,5,5] if !enctype[i] - end - ret = 0 - loop do - commands.clear - for i in 0...enctype.length - ch = chances[i] - ch = sprintf("%.1f",100.0*chances[i]/chancetotal) if chancetotal!=100 - if enctype[i][1]==enctype[i][2] - commands.push(_INTL("{1}% {2} (Lv.{3})", ch, - GameData::Species.get(enctype[i][0]).real_name, enctype[i][1])) - else - commands.push(_INTL("{1}% {2} (Lv.{3}-Lv.{4})", ch, - GameData::Species.get(enctype[i][0]).real_name, enctype[i][1], enctype[i][2])) - end - end - ret = pbCommands2(cmdwin,commands,-1,ret) - break if ret<0 - species = pbChooseSpeciesList(enctype[ret][0]) - next if !species - enctype[ret][0] = species - mLevel = PBExperience.maxLevel - params = ChooseNumberParams.new - params.setRange(1,mLevel) - params.setDefaultValue(enctype[ret][1]) - minlevel = pbMessageChooseNumber(_INTL("Set the minimum level."),params) - params = ChooseNumberParams.new - params.setRange(minlevel,mLevel) - params.setDefaultValue(minlevel) - maxlevel = pbMessageChooseNumber(_INTL("Set the maximum level."),params) - enctype[ret][1] = minlevel - enctype[ret][2] = maxlevel - end - cmdwin.dispose -end - -def pbEncounterEditorDensity(enc) - params = ChooseNumberParams.new - params.setRange(0,100) - params.setDefaultValue(enc[0][EncounterTypes::Land]) - enc[0][EncounterTypes::Land] = pbMessageChooseNumber( - _INTL("Set the density of Pokémon on land (default {1}).", - EncounterTypes::EnctypeDensities[EncounterTypes::Land]),params) - params = ChooseNumberParams.new - params.setRange(0,100) - params.setDefaultValue(enc[0][EncounterTypes::Cave]) - enc[0][EncounterTypes::Cave] = pbMessageChooseNumber( - _INTL("Set the density of Pokémon in caves (default {1}).", - EncounterTypes::EnctypeDensities[EncounterTypes::Cave]),params) - params = ChooseNumberParams.new - params.setRange(0,100) - params.setDefaultValue(enc[0][EncounterTypes::Water]) - enc[0][EncounterTypes::Water] = pbMessageChooseNumber( - _INTL("Set the density of Pokémon on water (default {1}).", - EncounterTypes::EnctypeDensities[EncounterTypes::Water]),params) - for i in 0...EncounterTypes::EnctypeCompileDens.length - t = EncounterTypes::EnctypeCompileDens[i] - next if !t || t==0 - enc[0][i] = enc[0][EncounterTypes::Land] if t==1 - enc[0][i] = enc[0][EncounterTypes::Cave] if t==2 - enc[0][i] = enc[0][EncounterTypes::Water] if t==3 - end -end - -def pbEncounterEditorMap(encdata,map) - enccmd = pbListWindow([]) - # This window displays the help text - enchelp = Window_UnformattedTextPokemon.new("") - enchelp.x = Graphics.width/2 - enchelp.y = 0 - enchelp.width = Graphics.width/2 - 32 - enchelp.height = 96 - enchelp.z = 99999 - mapinfos = load_data("Data/MapInfos.rxdata") - mapname = mapinfos[map].name - loop do - enc = encdata[map] - enchelp.text = _ISPRINTF("{1:03d}: {2:s}\r\nChoose a method",map,mapname) - choice = pbEncounterEditorTypes(enc,enccmd) - if !enc - enc = [EncounterTypes::EnctypeDensities.clone,[]] - encdata[map] = enc - end - if choice==-2 - pbEncounterEditorDensity(enc) - elsif choice==-1 - break - elsif choice==-3 - ret = pbNewEncounterType(enc) - if ret>=0 - enchelp.text = _ISPRINTF("{1:03d}: {2:s}\r\n{3:s}",map,mapname,EncounterTypes::Names[ret]) - pbEditEncounterType(enc,ret) + elsif ret > 0 # Edit an encounter set + this_set = maps[ret - 1] + case pbShowCommands(nil, [_INTL("Edit"), _INTL("Copy"), _INTL("Delete"), _INTL("Cancel")], 4) + when 0 # Edit + pbEncounterMapVersionEditor(GameData::Encounter.get(this_set[0], this_set[1])) + need_refresh = true + when 1 # Copy + new_map_ID = pbListScreen(_INTL("Copy to which map?"), MapLister.new(this_set[0])) + if new_map_ID > 0 + new_version = LimitProperty2.new(999).set(_INTL("version number"), 0) + if new_version && new_version >= 0 + if GameData::Encounter.exists?(new_map_ID, new_version) + pbMessage(_INTL("A set of encounters for map {1} version {2} already exists.", new_map_ID, new_version)) + else + types = [] + GameData::Encounter.get(this_set[0], this_set[1]).types.each_with_index do |enc_type, i| + next if !enc_type + types[i] = [] + enc_type.each { |slot| types[i].push(slot.clone) } + end + # Construct encounter hash + key = sprintf("%s_%d", new_map_ID, new_version).to_sym + encounter_hash = { + :id => key, + :map => new_map_ID, + :version => new_version, + :step_chances => GameData::Encounter.get(this_set[0], this_set[1]).step_chances.clone, + :types => types + } + GameData::Encounter::DATA[encounter_hash[:id]] = GameData::Encounter.new(encounter_hash) + maps.push([new_map_ID, new_version]) + maps.sort! { |a, b| (a[0] == b[0]) ? a[1] <=> b[1] : a[0] <=> b[0] } + ret = maps.index([new_map_ID, new_version]) + 1 + need_refresh = true + end + end + end + when 2 # Delete + if pbConfirmMessage(_INTL("Delete the encounter set for map {1} version {2}?", this_set[0], this_set[1])) + key = sprintf("%s_%d", this_set[0], this_set[1]).to_sym + GameData::Encounter::DATA.delete(key) + ret -= 1 + need_refresh = true + end end else - enchelp.text = _ISPRINTF("{1:03d}: {2:s}\r\n{3:s}",map,mapname,EncounterTypes::Names[choice]) - pbEditEncounterType(enc,choice) + break end end - if encdata[map][1].length==0 - encdata[map] = nil + if pbConfirmMessage(_INTL("Save changes?")) + GameData::Encounter.save + Compiler.write_encounters # Rewrite PBS file encounters.txt + else + GameData::Encounter.load end - enccmd.dispose - enchelp.dispose + list.dispose + help_window.dispose + Input.update +end + +# Lists the map ID, version number and defined encounter types for the given +# encounter data (a GameData::Encounter instance), and edits them. +def pbEncounterMapVersionEditor(enc_data) + map_infos = load_data("Data/MapInfos.rxdata") + commands = [] + enc_types = [] + list = pbListWindow([]) + help_window = Window_UnformattedTextPokemon.newWithSize(_INTL("Edit map's encounters"), + Graphics.width / 2, 0, Graphics.width / 2, 96) + help_window.z = 99999 + ret = 0 + need_refresh = true + loop do + if need_refresh + commands.clear + enc_types.clear + map_name = (map_infos[enc_data.map]) ? map_infos[enc_data.map].name : nil + if map_name + commands.push(_INTL("Map ID={1} ({2})", enc_data.map, map_name)) + else + commands.push(_INTL("Map ID={1}", enc_data.map)) + end + commands.push(_INTL("Version={1}", enc_data.version)) + enc_data.types.each_with_index do |enc_type, i| + next if !enc_type + commands.push(_INTL("{1} (x{2})", EncounterTypes::Names[i], enc_type.length)) + enc_types.push(i) + end + commands.push(_INTL("[Add new encounter type]")) + need_refresh = false + end + ret = pbCommands2(list, commands, -1, ret) + if ret == 0 # Edit map ID + old_map_ID = enc_data.map + new_map_ID = pbListScreen(_INTL("Choose a new map"), MapLister.new(old_map_ID)) + if new_map_ID > 0 && new_map_ID != old_map_ID + if GameData::Encounter.exists?(new_map_ID, enc_data.version) + pbMessage(_INTL("A set of encounters for map {1} version {2} already exists.", new_map_ID, enc_data.version)) + else + GameData::Encounter::DATA.delete(enc_data.id) + enc_data.map = new_map_ID + enc_data.id = sprintf("%s_%d", enc_data.map, enc_data.version).to_sym + GameData::Encounter::DATA[enc_data.id] = enc_data + need_refresh = true + end + end + elsif ret == 1 # Edit version number + old_version = enc_data.version + new_version = LimitProperty2.new(999).set(_INTL("version number"), old_version) + if new_version && new_version != old_version + if GameData::Encounter.exists?(enc_data.map, new_version) + pbMessage(_INTL("A set of encounters for map {1} version {2} already exists.", enc_data.map, new_version)) + else + GameData::Encounter::DATA.delete(enc_data.id) + enc_data.version = new_version + enc_data.id = sprintf("%s_%d", enc_data.map, enc_data.version).to_sym + GameData::Encounter::DATA[enc_data.id] = enc_data + need_refresh = true + end + end + elsif ret == commands.length - 1 # Add new encounter type + new_type_commands = [] + new_types = [] + EncounterTypes::Names.each_with_index do |new_type, i| + next if enc_data.types[i] + new_type_commands.push(new_type) + new_types.push(i) + end + if new_type_commands.length > 0 + chosen_type_cmd = pbShowCommands(nil, new_type_commands, -1) + if chosen_type_cmd >= 0 + new_type = new_types[chosen_type_cmd] + enc_data.step_chances[new_type] = 0 + enc_data.types[new_type] = [] + pbEncounterTypeEditor(enc_data, new_type) + enc_types.push(new_type) + ret = enc_types.sort.index(new_type) + 2 + need_refresh = true + end + else + pbMessage(_INTL("There are no unused encounter types to add.")) + end + elsif ret > 0 # Edit an encounter type (its step chance and slots) + this_type = enc_types[ret - 2] + case pbShowCommands(nil, [_INTL("Edit"), _INTL("Copy"), _INTL("Delete"), _INTL("Cancel")], 4) + when 0 # Edit + pbEncounterTypeEditor(enc_data, this_type) + need_refresh = true + when 1 # Copy + new_type_commands = [] + new_types = [] + EncounterTypes::Names.each_with_index do |new_type, i| + next if enc_data.types[i] + new_type_commands.push(new_type) + new_types.push(i) + end + if new_type_commands.length > 0 + chosen_type_cmd = pbMessage(_INTL("Choose an encounter type to copy to."), + new_type_commands, -1) + if chosen_type_cmd >= 0 + new_type = new_types[chosen_type_cmd] + enc_data.step_chances[new_type] = enc_data.step_chances[this_type] + enc_data.types[new_type] = [] + enc_data.types[this_type].each { |enc| enc_data.types[new_type].push(enc.clone) } + enc_types.push(new_type) + ret = enc_types.sort.index(new_type) + 2 + need_refresh = true + end + else + pbMessage(_INTL("There are no unused encounter types to copy to.")) + end + when 2 # Delete + if pbConfirmMessage(_INTL("Delete the encounter type {1}?", EncounterTypes::Names[this_type])) + enc_data.step_chances[this_type] = nil + enc_data.types[this_type] = nil + need_refresh = true + end + end + else + break + end + end + list.dispose + help_window.dispose + Input.update +end + +# Lists the step chance and encounter slots for the given encounter type in the +# given encounter data (a GameData::Encounter instance), and edits them. +def pbEncounterTypeEditor(enc_data, enc_type) + commands = [] + list = pbListWindow([]) + help_window = Window_UnformattedTextPokemon.newWithSize(_INTL("Edit encounter slots"), + Graphics.width / 2, 0, Graphics.width / 2, 96) + help_window.z = 99999 + ret = 0 + need_refresh = true + loop do + if need_refresh + commands.clear + commands.push(_INTL("Step chance={1}", enc_data.step_chances[enc_type] || 0)) + commands.push(_INTL("Encounter type={1}", EncounterTypes::Names[enc_type])) + if enc_data.types[enc_type] && enc_data.types[enc_type].length > 0 + enc_data.types[enc_type].each do |slot| + commands.push(EncounterSlotProperty.format(slot)) + end + end + commands.push(_INTL("[Add new slot]")) + need_refresh = false + end + ret = pbCommands2(list, commands, -1, ret) + if ret == 0 # Edit step chance + old_step_chance = enc_data.step_chances[enc_type] || 0 + new_step_chance = LimitProperty.new(180).set(_INTL("Step chance"), old_step_chance) + if new_step_chance != old_step_chance + enc_data.step_chances[enc_type] = new_step_chance + need_refresh = true + end + elsif ret == 1 # Edit encounter type + new_type_commands = [] + new_types = [] + chosen_type_cmd = 0 + EncounterTypes::Names.each_with_index do |type_name, i| + next if enc_data.types[i] && i != enc_type + new_type_commands.push(type_name) + new_types.push(i) + chosen_type_cmd = new_type_commands.length - 1 if i == enc_type + end + chosen_type_cmd = pbShowCommands(nil, new_type_commands, -1, chosen_type_cmd) + if chosen_type_cmd >= 0 && new_types[chosen_type_cmd] != enc_type + new_type = new_types[chosen_type_cmd] + enc_data.step_chances[new_type] = enc_data.step_chances[enc_type] + enc_data.step_chances[enc_type] = nil + enc_data.types[new_type] = enc_data.types[enc_type] + enc_data.types[enc_type] = nil + enc_type = new_type + need_refresh = true + end + elsif ret == commands.length - 1 # Add new encounter slot + new_slot_data = EncounterSlotProperty.set(EncounterTypes::Names[enc_type], nil) + if new_slot_data + enc_data.types[enc_type].push(new_slot_data) + need_refresh = true + end + elsif ret > 0 # Edit a slot + case pbShowCommands(nil, [_INTL("Edit"), _INTL("Copy"), _INTL("Delete"), _INTL("Cancel")], 4) + when 0 # Edit + old_slot_data = enc_data.types[enc_type][ret - 2] + new_slot_data = EncounterSlotProperty.set(EncounterTypes::Names[enc_type], old_slot_data.clone) + if new_slot_data && new_slot_data != old_slot_data + enc_data.types[enc_type][ret - 2] = new_slot_data + need_refresh = true + end + when 1 # Copy + enc_data.types[enc_type].insert(ret, enc_data.types[enc_type][ret - 2].clone) + ret += 1 + need_refresh = true + when 2 # Delete + if pbConfirmMessage(_INTL("Delete this encounter slot?")) + enc_data.types[enc_type][ret - 2] = nil + enc_data.types[enc_type].compact! + need_refresh = true + end + end + else + break + end + end + list.dispose + help_window.dispose Input.update end diff --git a/Data/Scripts/021_Debug/007_Editor_DataTypes.rb b/Data/Scripts/021_Debug/007_Editor_DataTypes.rb index da7200bd7..a7d933b8e 100644 --- a/Data/Scripts/021_Debug/007_Editor_DataTypes.rb +++ b/Data/Scripts/021_Debug/007_Editor_DataTypes.rb @@ -35,7 +35,7 @@ class UIntProperty def set(settingname,oldsetting) params = ChooseNumberParams.new params.setMaxDigits(@maxdigits) - params.setDefaultValue(oldsetting||0) + params.setDefaultValue(oldsetting || 0) return pbMessageChooseNumber(_INTL("Set the value for {1}.",settingname),params) end @@ -278,6 +278,31 @@ end +module SpeciesFormProperty + def self.set(_settingname,oldsetting) + ret = pbChooseSpeciesFormList(oldsetting || nil) + return ret || oldsetting + end + + def self.defaultValue + return nil + end + + def self.format(value) + if value && GameData::Species.exists?(value) + species_data = GameData::Species.get(value) + if species_data.form > 0 + return sprintf("%s_%d", species_data.real_name, species_data.form) + else + return species_data.real_name + end + end + return "-" + end +end + + + module TypeProperty def self.set(_settingname, oldsetting) ret = pbChooseTypeList(oldsetting || nil) @@ -1112,105 +1137,6 @@ end -module FormNamesProperty - def self.set(_settingname,oldsetting) - ret = oldsetting - cmdwin = pbListWindow([],200) - commands = [] - realcmds = [] - realcmds.push([_INTL("[ADD FORM]"),-1]) - for i in 0...oldsetting.length - realcmds.push([oldsetting[i],i]) - end - refreshlist = true; oldsel = -1 - cmd = [0,0] - loop do - if refreshlist - realcmds.sort! { |a,b| a[1]<=>b[1] } - commands = [] - for i in 0...realcmds.length - text = (realcmds[i][1]>=0) ? sprintf("#{realcmds[i][1]} - #{realcmds[i][0]}") : realcmds[i][0] - commands.push(text) - cmd[1] = i if oldsel>=0 && realcmds[i][1]==oldsel - end - end - refreshlist = false; oldsel = -1 - cmd = pbCommands3(cmdwin,commands,-1,cmd[1],true) - if cmd[0]==1 # Swap name up - if cmd[1]=0 && realcmds[cmd[1]+1][1]>=0 - realcmds[cmd[1]+1][1],realcmds[cmd[1]][1] = realcmds[cmd[1]][1],realcmds[cmd[1]+1][1] - refreshlist = true - end - elsif cmd[0]==2 # Swap name down - if cmd[1]>0 && realcmds[cmd[1]][1]>=0 && realcmds[cmd[1]-1][1]>=0 - realcmds[cmd[1]-1][1],realcmds[cmd[1]][1] = realcmds[cmd[1]][1],realcmds[cmd[1]-1][1] - refreshlist = true - end - elsif cmd[0]==0 - if cmd[1]>=0 - entry = realcmds[cmd[1]] - if entry[1]<0 # Add new form - newname = pbMessageFreeText(_INTL("Choose a form name (no commas)."),"",false,250) - if newname!="" - realcmds.push([newname,realcmds.length-1]) - refreshlist = true - end - else # Edit form name - cmd2 = pbMessage(_INTL("\\ts[]Do what with this form name?"), - [_INTL("Rename"),_INTL("Delete"),_INTL("Cancel")],3) - if cmd2==0 - newname = pbMessageFreeText(_INTL("Choose a form name (no commas)."),entry[0],false,250) - if newname!="" - realcmds[cmd[1]][0] = newname - refreshlist = true - end - elsif cmd2==1 - realcmds[cmd[1]] = nil - realcmds.compact! - cmd[1] = [cmd[1],realcmds.length-1].min - refreshlist = true - end - end - else - cmd2 = pbMessage(_INTL("Save changes?"), - [_INTL("Yes"),_INTL("No"),_INTL("Cancel")],3) - if cmd2==0 || cmd2==1 - if cmd2==0 - for i in 0...realcmds.length - if realcmds[i][1]<0 - realcmds[i] = nil - else - realcmds[i] = realcmds[i][0] - end - end - realcmds.compact! - ret = realcmds - end - break - end - end - end - end - cmdwin.dispose - return ret - end - - def self.defaultValue - return [] - end - - def self.format(value) - ret = "" - for i in 0...value.length - ret << "," if i>0 - ret << sprintf("#{value[i]}") - end - return ret - end -end - - - class EvolutionsProperty def initialize @methods = [] @@ -1464,29 +1390,69 @@ end +module EncounterSlotProperty + def self.set(setting_name, data) + max_level = PBExperience.maxLevel + if !data + data = [20, nil, 5, 5] + GameData::Species.each do |species_data| + data[1] = species_data.species + break + end + end + data[3] = data[2] if !data[3] + properties = [ + [_INTL("Probability"), NonzeroLimitProperty.new(999), _INTL("Relative probability of choosing this slot.")], + [_INTL("Species"), SpeciesFormProperty, _INTL("A Pokémon species/form.")], + [_INTL("Minimum level"), NonzeroLimitProperty.new(max_level), _INTL("Minimum level of this species (1-{1}).", max_level)], + [_INTL("Maximum level"), NonzeroLimitProperty.new(max_level), _INTL("Maximum level of this species (1-{1}).", max_level)] + ] + pbPropertyList(setting_name, data, properties, false) + if data[2] > data[3] + data[3], data[2] = data[2], data[3] + end + return data + end + + def self.defaultValue + return nil + end + + def self.format(value) + return "-" if !value + species_data = GameData::Species.get(value[1]) + if species_data.form > 0 + if value[2] == value[3] + return sprintf("%d, %s_%d (Lv.%d)", value[0], + species_data.real_name, species_data.form, value[2]) + end + return sprintf("%d, %s_%d (Lv.%d-%d)", value[0], + species_data.real_name, species_data.form, value[2], value[3]) + end + if value[2] == value[3] + return sprintf("%d, %s (Lv.%d)", value[0], species_data.real_name, value[2]) + end + return sprintf("%d, %s (Lv.%d-%d)", value[0], species_data.real_name, value[2], value[3]) + end +end + + + #=============================================================================== # Core property editor script #=============================================================================== def pbPropertyList(title,data,properties,saveprompt=false) viewport = Viewport.new(0,0,Graphics.width,Graphics.height) viewport.z = 99999 - list = pbListWindow([],Graphics.width*5/10) + list = pbListWindow([], Graphics.width / 2) list.viewport = viewport list.z = 2 - title = Window_UnformattedTextPokemon.new(title) - title.x = list.width - title.y = 0 - title.width = Graphics.width*5/10 - title.height = 64 - title.viewport = viewport - title.z = 2 - desc = Window_UnformattedTextPokemon.new("") - desc.x = list.width - desc.y = title.height - desc.width = Graphics.width*5/10 - desc.height = Graphics.height-title.height - desc.viewport = viewport - desc.z = 2 + title = Window_UnformattedTextPokemon.newWithSize(title, + list.width, 0, Graphics.width / 2, 64, viewport) + title.z = 2 + desc = Window_UnformattedTextPokemon.newWithSize("", + list.width, title.height, Graphics.width / 2, Graphics.height - title.height, viewport) + desc.z = 2 selectedmap = -1 retval = nil commands = [] diff --git a/Data/Scripts/021_Debug/008_Editor_Listers.rb b/Data/Scripts/021_Debug/008_Editor_Listers.rb index dfc9845ff..6ec7742f4 100644 --- a/Data/Scripts/021_Debug/008_Editor_Listers.rb +++ b/Data/Scripts/021_Debug/008_Editor_Listers.rb @@ -1,8 +1,8 @@ #=============================================================================== # Core lister script #=============================================================================== -def pbListWindow(cmds,width=Graphics.width/2) - list = Window_CommandPokemon.newWithSize(cmds,0,0,width,Graphics.height) +def pbListWindow(cmds, width = Graphics.width / 2) + list = Window_CommandPokemon.newWithSize(cmds, 0, 0, width, Graphics.height) list.index = 0 list.rowHeight = 24 pbSetSmallFont(list.contents) @@ -16,13 +16,9 @@ def pbListScreen(title,lister) list = pbListWindow([]) list.viewport = viewport list.z = 2 - title = Window_UnformattedTextPokemon.new(title) - title.x = Graphics.width/2 - title.y = 0 - title.width = Graphics.width-title.x - title.height = 64 - title.viewport = viewport - title.z = 2 + title = Window_UnformattedTextPokemon.newWithSize(title, + Graphics.width / 2, 0, Graphics.width / 2, 64, viewport) + title.z = 2 lister.setViewport(viewport) selectedmap = -1 commands = lister.commands @@ -62,18 +58,14 @@ def pbListScreen(title,lister) end def pbListScreenBlock(title,lister) - viewport = Viewport.new(0,0,Graphics.width,Graphics.height) + viewport = Viewport.new(0, 0, Graphics.width, Graphics.height) viewport.z = 99999 - list = pbListWindow([],Graphics.width/2) + list = pbListWindow([], Graphics.width / 2) list.viewport = viewport list.z = 2 - title = Window_UnformattedTextPokemon.new(title) - title.x = Graphics.width/2 - title.y = 0 - title.width = Graphics.width-title.x - title.height = 64 - title.viewport = viewport - title.z = 2 + title = Window_UnformattedTextPokemon.newWithSize(title, + Graphics.width / 2, 0, Graphics.width - title.x, 64, viewport) + title.z = 2 lister.setViewport(viewport) selectedmap = -1 commands = lister.commands diff --git a/Data/Scripts/021_Debug/009_Editor_Utilities.rb b/Data/Scripts/021_Debug/009_Editor_Utilities.rb index fb6012bb2..ab5423bb3 100644 --- a/Data/Scripts/021_Debug/009_Editor_Utilities.rb +++ b/Data/Scripts/021_Debug/009_Editor_Utilities.rb @@ -121,6 +121,15 @@ def pbChooseSpeciesList(default = nil) return pbChooseList(commands, default, nil, -1) end +def pbChooseSpeciesFormList(default = nil) + commands = [] + GameData::Species.each do |s| + name = (s.form == 0) ? s.real_name : sprintf("%s_%d", s.real_name, s.form) + commands.push([s.id_number, name, s.id]) + end + return pbChooseList(commands, default, nil, -1) +end + # Displays a list of all moves, and returns the ID of the move selected (or nil # if the selection was canceled). "default", if specified, is the ID of the move # to initially select. Pressing Input::A will toggle the list sorting between diff --git a/Data/Scripts/021_Debug/011_Editor_MapConnections.rb b/Data/Scripts/021_Debug/011_Editor_MapConnections.rb index 1781806b3..63b0bb6d8 100644 --- a/Data/Scripts/021_Debug/011_Editor_MapConnections.rb +++ b/Data/Scripts/021_Debug/011_Editor_MapConnections.rb @@ -335,7 +335,6 @@ class MapScreenScene @sprites["title"].viewport=@viewport @sprites["title"].z=2 @mapinfos=load_data("Data/MapInfos.rxdata") - @encdata=pbLoadEncountersData conns=MapFactoryHelper.getMapConnections @mapconns=[] for c in conns @@ -361,7 +360,6 @@ class MapScreenScene helptext+=_INTL("S: Go to another map\r\n") helptext+=_INTL("Click to select a map\r\n") helptext+=_INTL("Double-click: Edit map's metadata\r\n") - helptext+=_INTL("E: Edit map's encounters\r\n") helptext+=_INTL("Drag map to move it\r\n") helptext+=_INTL("Arrow keys/drag canvas: Move around canvas") title=Window_UnformattedTextPokemon.new(helptext) @@ -556,8 +554,6 @@ class MapScreenScene @sprites["selsprite"].othersprite=nil @selmapid=-1 end - elsif Input.triggerex?("E"[0]) - pbEncounterEditorMap(@encdata,@selmapid) if @selmapid>=0 elsif Input.trigger?(Input::F5) helpWindow end @@ -573,9 +569,8 @@ class MapScreenScene if pbConfirmMessage(_INTL("Save changes?")) serializeConnectionData MapFactoryHelper.clear - save_data(@encdata,"Data/encounters.dat") - $PokemonTemp.encountersData = nil - Compiler.write_encounters + else + GameData.Encounter.load end break if pbConfirmMessage(_INTL("Exit from the editor?")) end diff --git a/Data/Scripts/022_Compiler/002_Compiler_CompilePBS.rb b/Data/Scripts/022_Compiler/002_Compiler_CompilePBS.rb index 7a9cb12c8..96cf5d3de 100644 --- a/Data/Scripts/022_Compiler/002_Compiler_CompilePBS.rb +++ b/Data/Scripts/022_Compiler/002_Compiler_CompilePBS.rb @@ -827,107 +827,194 @@ module Compiler # Compile wild encounter data #============================================================================= def compile_encounters - lines = [] - linenos = [] - FileLineData.file = "PBS/encounters.txt" - File.open("PBS/encounters.txt","rb") { |f| - lineno = 1 - f.each_line { |line| - if lineno==1 && line[0]==0xEF && line[1]==0xBB && line[2]==0xBF - line = line[3,line.length-3] + new_format = nil + encounter_hash = nil + step_chances = nil + need_step_chances = false # Not needed for new format only + probabilities = nil + current_type = -1 + expected_lines = 0 + max_level = PBExperience.maxLevel + pbCompilerEachPreppedLine("PBS/encounters.txt") { |line, line_no| + next if line.length == 0 + if expected_lines > 0 && line[/^\d+,/] && new_format # Species line + values = line.split(',') + if !values || values.length < 3 + raise _INTL("Expected a species entry line for encounter type {1} for map '{2}', got \"{3}\" instead.\r\n{4}", + EncounterTypes::Names[current_type], encounter_hash[:map], line, FileLineData.linereport) end - line = prepline(line) - if line.length!=0 - lines[lines.length] = line - linenos[linenos.length] = lineno + values = pbGetCsvRecord(line, line_no, [0, "vevV", nil, :Species]) + values[3] = values[2] if !values[3] + if values[2] > max_level + raise _INTL("Level number {1} is not valid (max. {2}).\r\n{3}", values[2], max_level, FileLineData.linereport) + elsif values[3] > max_level + raise _INTL("Level number {1} is not valid (max. {2}).\r\n{3}", values[3], max_level, FileLineData.linereport) + elsif values[2] > values[3] + raise _INTL("Minimum level is greater than maximum level: {1}\r\n{2}", line, FileLineData.linereport) end - lineno += 1 - } - } - encounters = {} - thisenc = nil - needdensity = false - lastmapid = -1 - i = 0 - while i 0 && !new_format # Expect a species line and nothing else + values = line.split(',') + if !values || values.length < 2 + raise _INTL("Expected a species entry line for encounter type {1} for map '{2}', got \"{3}\" instead.\r\n{4}", + EncounterTypes::Names[current_type], encounter_hash[:map], line, FileLineData.linereport) end - if thisenc && (thisenc[1][EncounterTypes::Land] || - thisenc[1][EncounterTypes::LandMorning] || - thisenc[1][EncounterTypes::LandDay] || - thisenc[1][EncounterTypes::LandNight] || - thisenc[1][EncounterTypes::BugContest]) && - thisenc[1][EncounterTypes::Cave] - raise _INTL("Can't define both Land and Cave encounters in the same area (map ID '{1}').",mapid) + values = pbGetCsvRecord(line, line_no, [0, "evV", :Species]) + values[2] = values[1] if !values[2] + if values[1] > max_level + raise _INTL("Level number {1} is not valid (max. {2}).\r\n{3}", values[1], max_level, FileLineData.linereport) + elsif values[2] > max_level + raise _INTL("Level number {1} is not valid (max. {2}).\r\n{3}", values[2], max_level, FileLineData.linereport) + elsif values[1] > values[2] + raise _INTL("Minimum level is greater than maximum level: {1}\r\n{2}", line, FileLineData.linereport) end - thisenc = [EncounterTypes::EnctypeDensities.clone,[]] - encounters[mapid.to_i] = thisenc - needdensity = true - i += 1 - next - end - enc = findIndex(EncounterTypes::Names) { |val| val==line } - if enc>=0 - needdensity = false - enclines = EncounterTypes::EnctypeChances[enc].length - encarray = [] - j = i+1; k = 0 - while j b[1].to_s : b[0] <=> a[0] } end - splitarr[2] = splitarr[1] if splitarr.length==2 - splitarr[1] = splitarr[1].to_i - splitarr[2] = splitarr[2].to_i - maxlevel = PBExperience.maxLevel - if splitarr[1]<=0 || splitarr[1]>maxlevel - raise _INTL("Level number is not valid: {1}\r\n{2}",splitarr[1],FileLineData.linereport) - end - if splitarr[2]<=0 || splitarr[2]>maxlevel - raise _INTL("Level number is not valid: {1}\r\n{2}",splitarr[2],FileLineData.linereport) - end - if splitarr[1]>splitarr[2] - raise _INTL("Minimum level is greater than maximum level: {1}\r\n{2}",line,FileLineData.linereport) - end - splitarr[0] = parseSpecies(splitarr[0]) - encarray.push(splitarr) - thisenc[1][enc] = encarray - j += 1; k += 1 + GameData::Encounter::DATA[encounter_hash[:id]] = GameData::Encounter.new(encounter_hash) end - if j==lines.length && k=3 - for j in 0...EncounterTypes::EnctypeChances.length - next if !EncounterTypes::EnctypeChances[j] || - EncounterTypes::EnctypeChances[j].length==0 - next if EncounterTypes::EnctypeCompileDens[j]==0 - thisenc[0][j] = nums[EncounterTypes::EnctypeCompileDens[j]-1].to_i + step_chances = [] + # Construct encounter hash + encounter_hash = { + :id => key, + :map => map_number, + :version => map_version, + :step_chances => step_chances, + :types => [] + } + current_type = -1 + need_step_chances = true + expected_lines = 0 + elsif line[/^(\d+)$/] # Map ID line (old format) + if new_format == true + raise _INTL("Can't mix old and new formats.\r\n{1}", FileLineData.linereport) + end + new_format = false + map_number = $~[1].to_i + # Add map encounter's data to records + if encounter_hash + encounter_hash[:types].each do |encounters| + next if !encounters || encounters.length == 0 + encounters.each_with_index do |enc, i| + next if !enc + encounters.each_with_index do |other_enc, j| + next if i == j || !other_enc + next if enc[1] != other_enc[1] || enc[2] != other_enc[2] || enc[3] != other_enc[3] + enc[0] += other_enc[0] + encounters[j] = nil + end + end + encounters.compact! + encounters.sort! { |a, b| (a[0] == b[0]) ? a[1].to_s <=> b[1].to_s : b[0] <=> a[0] } end - else - raise _INTL("Wrong syntax for densities in encounters.txt; got \"{1}\"\r\n{2}",line,FileLineData.linereport) + GameData::Encounter::DATA[encounter_hash[:id]] = GameData::Encounter.new(encounter_hash) + end + # Raise an error if a map/version combo is used twice + key = sprintf("%s_0", map_number).to_sym + if GameData::Encounter::DATA[key] + raise _INTL("Encounters for map '{1}' are defined twice.\r\n{2}", map_number, FileLineData.linereport) + end + step_chances = EncounterTypes::Chances_Per_Step.clone + # Construct encounter hash + encounter_hash = { + :id => key, + :map => map_number, + :version => 0, + :step_chances => step_chances, + :types => [] + } + current_type = -1 + need_step_chances = true + elsif !encounter_hash # File began with something other than a map ID line + raise _INTL("Expected a map number, got \"{1}\" instead.\r\n{2}", line, FileLineData.linereport) + elsif line[/^(\d+)\s*,/] && !new_format # Step chances line + if !need_step_chances + raise _INTL("Encounter densities are defined twice or\r\nnot immediately for map '{1}'.\r\n{2}", + encounter_hash[:map], FileLineData.linereport) + end + need_step_chances = false + values = pbGetCsvRecord(line, line_no, [0, "vvv"]) + for type in 0...step_chances.length + next if EncounterTypes::Kinds[type] == 0 + step_chances[type] = values[EncounterTypes::Kinds[type] - 1] end - i += 1 else - raise _INTL("Undefined encounter type {1}, expected one of the following:\r\n{2}\r\n{3}",line,EncounterTypes::Names.inspect,FileLineData.linereport) + # Check if line is an encounter method name or not + values = line.split(',') + values.collect! { |v| v.strip } + current_type = findIndex(EncounterTypes::Names) { |val| val == values[0] } + if current_type >= 0 # Start of a new encounter method + need_step_chances = false + if values[1] && !values[1].empty? + step_chances[current_type] = values[1].to_i + elsif new_format + step_chances[current_type] = 0 + end + probabilities = EncounterTypes::Probabilities[current_type].clone + expected_lines = probabilities.length + encounter_hash[:types][current_type] = [] + else + raise _INTL("Undefined encounter type \"{1}\" for map '{2}'.\r\n{2}", + line, encounter_hash[:map], FileLineData.linereport) + end end + } + if expected_lines > 0 && !new_format + raise _INTL("Not enough encounter lines given for encounter type {1} for map '{2}' (expected {3}).\r\n{4}", + EncounterTypes::Names[current_type], encounter_hash[:map], probabilities.length, FileLineData.linereport) end - save_data(encounters,"Data/encounters.dat") + # Add last map's encounter data to records + if encounter_hash + encounter_hash[:types].each do |encounters| + next if !encounters || encounters.length == 0 + encounters.each_with_index do |enc, i| + next if !enc + encounters.each_with_index do |other_enc, j| + next if i == j || !other_enc + next if enc[1] != other_enc[1] || enc[2] != other_enc[2] || enc[3] != other_enc[3] + enc[0] += other_enc[0] + encounters[j] = nil + end + end + encounters.compact! + encounters.sort! { |a, b| (a[0] == b[0]) ? a[1].to_s <=> b[1].to_s : b[0] <=> a[0] } + end + GameData::Encounter::DATA[encounter_hash[:id]] = GameData::Encounter.new(encounter_hash) + end + # Save all data + GameData::Encounter.save + Graphics.update end #============================================================================= diff --git a/Data/Scripts/022_Compiler/003_Compiler_WritePBS.rb b/Data/Scripts/022_Compiler/003_Compiler_WritePBS.rb index 08d2a8e1e..f4e1a8f97 100644 --- a/Data/Scripts/022_Compiler/003_Compiler_WritePBS.rb +++ b/Data/Scripts/022_Compiler/003_Compiler_WritePBS.rb @@ -507,35 +507,29 @@ module Compiler # Save wild encounter data to PBS file #=============================================================================== def write_encounters - encdata = pbLoadEncountersData - return if !encdata - mapinfos = load_data("Data/MapInfos.rxdata") - File.open("PBS/encounters.txt","wb") { |f| + map_infos = load_data("Data/MapInfos.rxdata") + File.open("PBS/encounters.txt", "wb") { |f| add_PBS_header_to_file(f) - sortedkeys = encdata.keys.sort - for i in sortedkeys - next if !encdata[i] - e = encdata[i] - mapname = "" - if mapinfos[i] - map = mapinfos[i].name - mapname = " # #{map}" - end + GameData::Encounter.each do |encounter_data| f.write("\#-------------------------------\r\n") - f.write(sprintf("%03d%s\r\n",i,mapname)) - f.write(sprintf("%d,%d,%d\r\n",e[0][EncounterTypes::Land], - e[0][EncounterTypes::Cave],e[0][EncounterTypes::Water])) - for j in 0...e[1].length - enc = e[1][j] - next if !enc - f.write(sprintf("%s\r\n",EncounterTypes::Names[j])) - for k in 0...EncounterTypes::EnctypeChances[j].length - next if !enc[k] - encentry = enc[k] - if encentry[1]==encentry[2] - f.write(sprintf(" %s,%d\r\n",encentry[0],encentry[1])) + map_name = (map_infos[encounter_data.map]) ? " # #{map_infos[encounter_data.map].name}" : "" + if encounter_data.version > 0 + f.write(sprintf("[%03d,%d]%s\r\n", encounter_data.map, encounter_data.version, map_name)) + else + f.write(sprintf("[%03d]%s\r\n", encounter_data.map, map_name)) + end + encounter_data.types.each_with_index do |entries, type| + next if !entries || entries.length == 0 + if encounter_data.step_chances[type] && encounter_data.step_chances[type] > 0 + f.write(sprintf("%s,%d\r\n", EncounterTypes::Names[type], encounter_data.step_chances[type])) + else + f.write(sprintf("%s\r\n", EncounterTypes::Names[type])) + end + entries.each do |entry| + if entry[2] == entry[3] + f.write(sprintf(" %d,%s,%d\r\n", entry[0], entry[1], entry[2])) else - f.write(sprintf(" %s,%d,%d\r\n",encentry[0],encentry[1],encentry[2])) + f.write(sprintf(" %d,%s,%d,%d\r\n", entry[0], entry[1], entry[2], entry[3])) end end end diff --git a/Data/Scripts/022_Compiler/005_Compiler_Sprite renamer.rb b/Data/Scripts/022_Compiler/005_Compiler_Sprite renamer.rb index 17988909f..52e3e7e71 100644 --- a/Data/Scripts/022_Compiler/005_Compiler_Sprite renamer.rb +++ b/Data/Scripts/022_Compiler/005_Compiler_Sprite renamer.rb @@ -215,7 +215,7 @@ module Compiler end def convert_files - return if !pbConfirmMessage("Do you want to check for Pokémon graphics/cries and item icons that need renaming?") + return if !pbConfirmMessage("Check for Pokémon/item/trainer files that need renaming?") # Rename and move Pokémon sprites/icons dest_dir = "Graphics/Pokemon/" Dir.mkdir(dest_dir) if !FileTest.directory?(dest_dir) diff --git a/PBS/encounters.txt b/PBS/encounters.txt index 23964a3af..16ea7231c 100644 --- a/PBS/encounters.txt +++ b/PBS/encounters.txt @@ -1,471 +1,291 @@ # See the documentation on the wiki to learn how to edit this file. #------------------------------- -002 # Lappet Town -25,10,10 -Water - TENTACOOL,14,19 - MANTYKE,15,16 - REMORAID,14,16 - REMORAID,14,16 - REMORAID,14,16 +[002] # Lappet Town +Water,10 + 60,TENTACOOL,14,19 + 30,MANTYKE,15,16 + 10,REMORAID,14,16 OldRod - MAGIKARP,16,19 - MAGIKARP,16,19 + 100,MAGIKARP,16,19 GoodRod - BARBOACH,17,18 - SHELLDER,16,19 - KRABBY,15,16 + 60,BARBOACH,17,18 + 20,KRABBY,15,16 + 20,SHELLDER,16,19 SuperRod - CHINCHOU,17,19 - QWILFISH,16,19 - CORSOLA,15,18 - STARYU,15,17 - STARYU,15,17 + 40,CHINCHOU,17,19 + 40,QWILFISH,16,19 + 15,CORSOLA,15,18 + 5,STARYU,15,17 #------------------------------- -005 # Route 1 -25,10,10 -Land - RATTATA,11,14 - PIDGEY,11,14 - RATTATA,11,14 - PIDGEY,11,14 - RATTATA,11,14 - PIDGEY,11,14 - RATTATA,11,13 - PIDGEY,11,13 - RATTATA,11,13 - PIDGEY,11,13 - RATTATA,14 - PIDGEY,14 -LandNight - RATTATA,10,14 - HOOTHOOT,10,13 - RATTATA,10,14 - HOOTHOOT,10,13 - SPINARAK,8,12 - SPINARAK,8,12 - RATTATA,10,14 - HOOTHOOT,10,14 - RATTATA,10,14 - HOOTHOOT,10,14 - RATTATA,15 - HOOTHOOT,14 +[005] # Route 1 +Land,25 + 40,PIDGEY,11,14 + 40,RATTATA,11,14 + 9,PIDGEY,11,13 + 9,RATTATA,11,13 + 1,PIDGEY,14 + 1,RATTATA,14 +LandNight,25 + 39,RATTATA,10,14 + 30,HOOTHOOT,10,13 + 20,SPINARAK,8,12 + 9,HOOTHOOT,10,14 + 1,HOOTHOOT,14 + 1,RATTATA,15 #------------------------------- -021 # Route 2 -25,10,10 -Land - RATTATA,12,15 - RATTATA,12,15 - RATTATA,12,15 - POOCHYENA,11,15 - POOCHYENA,11,15 - POOCHYENA,11,15 - SHINX,10,12 - SHINX,10,12 - SHINX,10,11 - SHINX,10,11 - SHINX,10,11 - SHINX,10,11 -Water - MAGIKARP,7,10 - GOLDEEN,11,14 - STARYU,12,15 - STARYU,12,15 - STARYU,12,15 +[021] # Route 2 +Land,25 + 50,RATTATA,12,15 + 30,POOCHYENA,11,15 + 10,SHINX,10,12 + 10,SHINX,10,11 +Water,10 + 60,MAGIKARP,7,10 + 30,GOLDEEN,11,14 + 10,STARYU,12,15 OldRod - MAGIKARP,7,10 - MAGIKARP,9,15 + 70,MAGIKARP,7,10 + 30,MAGIKARP,9,15 GoodRod - GOLDEEN,12,14 - FINNEON,12,15 - MAGIKARP,12,17 + 60,GOLDEEN,12,14 + 20,FINNEON,12,15 + 20,MAGIKARP,12,17 SuperRod - GOLDEEN,12,14 - FINNEON,12,15 - STARYU,12,15 - STARYU,14,17 - STARYU,14,17 + 40,FINNEON,12,15 + 40,GOLDEEN,12,14 + 15,STARYU,12,15 + 5,STARYU,14,17 HeadbuttLow - PINECO,11,13 - LEDYBA,6,8 - PINECO,11,13 - SPINARAK,9,12 - LEDYBA,6,8 - SPINARAK,9,12 - SPINARAK,9,12 - MUNCHLAX,11,14 + 50,PINECO,11,13 + 30,LEDYBA,6,8 + 19,SPINARAK,9,12 + 1,MUNCHLAX,11,14 HeadbuttHigh - PINECO,11,13 - WURMPLE,6,8 - PINECO,11,13 - SPINARAK,9,12 - WURMPLE,6,8 - SPINARAK,9,12 - SPINARAK,9,12 - SPINARAK,9,12 + 50,PINECO,11,13 + 30,WURMPLE,6,8 + 20,SPINARAK,9,12 #------------------------------- -028 # Natural Park -25,10,10 -Land - CATERPIE,10 - WEEDLE,10 - PIDGEY,10,14 - PIDGEY,12,14 - SUNKERN,12 - SUNKERN,12 - METAPOD,10 - KAKUNA,10 - PIDGEY,10,14 - PIDGEY,12,14 - PIDGEY,10,14 - PIDGEY,12,14 -LandMorning - CATERPIE,10,12 - WEEDLE,10,12 - PIDGEY,10,14 - PIDGEY,10,14 - METAPOD,10 - KAKUNA,10 - METAPOD,10 - KAKUNA,10 - CATERPIE,10,12 - WEEDLE,10,12 - CATERPIE,10,12 - WEEDLE,10,12 -LandNight - HOOTHOOT,10,14 - SPINARAK,10,15 - HOOTHOOT,10,14 - SPINARAK,10,15 - PINECO,9,13 - PINECO,9,13 - NATU,12,14 - NATU,12,14 - DROWZEE,9,15 - DROWZEE,9,15 - DROWZEE,9,15 - DROWZEE,9,15 -BugContest - CATERPIE,7,18 - WEEDLE,7,18 - METAPOD,9,18 - KAKUNA,9,18 - PARAS,10,17 - VENONAT,10,16 - BUTTERFREE,12,15 - BEEDRILL,12,15 - SCYTHER,13,14 - PINSIR,13,14 - SCYTHER,13,14 - PINSIR,13,14 +[028] # Natural Park +Land,25 + 20,CATERPIE,10 + 20,SUNKERN,12 + 20,WEEDLE,10 + 15,PIDGEY,10,14 + 15,PIDGEY,12,14 + 5,KAKUNA,10 + 5,METAPOD,10 +LandNight,25 + 30,HOOTHOOT,10,14 + 30,SPINARAK,10,15 + 20,PINECO,9,13 + 10,DROWZEE,9,15 + 10,NATU,12,14 +LandMorning,25 + 25,CATERPIE,10,12 + 25,WEEDLE,10,12 + 20,PIDGEY,10,14 + 15,KAKUNA,10 + 15,METAPOD,10 +BugContest,25 + 20,CATERPIE,7,18 + 20,WEEDLE,7,18 + 10,KAKUNA,9,18 + 10,METAPOD,9,18 + 10,PARAS,10,17 + 10,VENONAT,10,16 + 5,BEEDRILL,12,15 + 5,BUTTERFREE,12,15 + 5,PINSIR,13,14 + 5,SCYTHER,13,14 #------------------------------- -031 # Route 3 -25,10,10 -Land - NIDORANfE,12,15 - NIDORANmA,12,15 - NIDORANfE,12,15 - NIDORANmA,12,15 - PIKACHU,14,17 - PIKACHU,14,17 - PONYTA,13,15 - PONYTA,13,15 - EEVEE,15 - EEVEE,15 - EEVEE,15 - EEVEE,15 -Water - SURSKIT,13,14 - LOTAD,14 - LOTAD,14 - LOTAD,15 - LOTAD,15 -RockSmash - NOSEPASS,13,14 - GEODUDE,12,15 - GEODUDE,12,15 - GEODUDE,12,15 - GEODUDE,12,15 +[031] # Route 3 +Land,25 + 30,NIDORANfE,12,15 + 30,NIDORANmA,12,15 + 20,PIKACHU,14,17 + 10,EEVEE,15 + 10,PONYTA,13,15 +Water,10 + 60,SURSKIT,13,14 + 35,LOTAD,14 + 5,LOTAD,15 OldRod - MAGIKARP,6,11 - MAGIKARP,10,17 + 70,MAGIKARP,6,11 + 30,MAGIKARP,10,17 GoodRod - POLIWAG,12,15 - PSYDUCK,11,14 - WOOPER,13,17 + 60,POLIWAG,12,15 + 20,PSYDUCK,11,14 + 20,WOOPER,13,17 SuperRod - CHINCHOU,11,12 - REMORAID,12,14 - LUVDISC,10,16 - LUVDISC,10,16 - LUVDISC,10,16 + 40,CHINCHOU,11,12 + 40,REMORAID,12,14 + 20,LUVDISC,10,16 +RockSmash + 60,NOSEPASS,13,14 + 40,GEODUDE,12,15 HeadbuttLow - PINECO,14,17 - COMBEE,15,17 - PINECO,14,16 - HERACROSS,16,18 - COMBEE,15,16 - HERACROSS,16,17 - HERACROSS,16,17 - MUNCHLAX,13,18 + 30,PINECO,14,17 + 25,COMBEE,15,17 + 20,PINECO,14,16 + 10,HERACROSS,16,18 + 9,HERACROSS,16,17 + 5,COMBEE,15,16 + 1,MUNCHLAX,13,18 HeadbuttHigh - SEEDOT,14,17 - SHROOMISH,14,17 - SEEDOT,14,17 - BURMY,12,15 - SHROOMISH,14,17 - BURMY,12,15 - BURMY,12,15 - BURMY,12,15 + 50,SEEDOT,14,17 + 30,SHROOMISH,14,17 + 20,BURMY,12,15 #------------------------------- -034 # Ice Cave -25,10,10 -Cave - SWINUB,16,18 - SWINUB,16,18 - SNEASEL,14,16 - SNEASEL,14,16 - SNORUNT,12,15 - SNORUNT,12,15 - SNOVER,14 - SNOVER,14 - SMOOCHUM,11,14 - SMOOCHUM,11,14 - SMOOCHUM,11,14 - SMOOCHUM,11,14 +[034] # Ice Cave +Cave,10 + 40,SWINUB,16,18 + 20,SNEASEL,14,16 + 20,SNORUNT,12,15 + 10,SMOOCHUM,11,14 + 10,SNOVER,14 #------------------------------- -039 # Route 4 -25,10,10 -Land - SHELLOS_1,12,15 - SHELLOS_1,12,15 - SHELLOS_1,12,15 - GRIMER,13,15 - GRIMER,13,15 - GRIMER,13,15 - GRIMER,13,15 - GRIMER,13,15 - MURKROW,12,14 - MURKROW,12,14 - MURKROW,12,14 - MURKROW,12,14 +[039] # Route 4 +Land,25 + 50,SHELLOS_1,12,15 + 40,GRIMER,13,15 + 10,MURKROW,12,14 #------------------------------- -041 # Route 5 -25,10,10 -Land - GRIMER,13,15 - GRIMER,13,15 - GRIMER,13,15 - SPEAROW,13,16 - SPEAROW,13,16 - SPEAROW,13,16 - SPEAROW,13,16 - SPEAROW,13,16 - SLUGMA,13,14 - SLUGMA,13,14 - SLUGMA,13,14 - SLUGMA,13,14 +[041] # Route 5 +Land,25 + 50,GRIMER,13,15 + 40,SPEAROW,13,16 + 10,SLUGMA,13,14 #------------------------------- -044 # Route 6 -25,10,10 -Land - SHELLOS_1,12,15 - SHELLOS_1,12,15 - SHELLOS_1,12,15 - GRIMER,13,15 - GRIMER,13,15 - GRIMER,13,15 - GRIMER,13,15 - GRIMER,13,15 - MURKROW,12,14 - MURKROW,12,14 - MURKROW,12,14 - MURKROW,12,14 +[044] # Route 6 +Land,25 + 50,SHELLOS_1,12,15 + 40,GRIMER,13,15 + 10,MURKROW,12,14 #------------------------------- -047 # Route 7 -25,10,10 -Land - SHELLOS,12,15 - SHELLOS,12,15 - SHELLOS,12,15 - BIDOOF,14,17 - BIDOOF,14,17 - BIDOOF,14,17 - MURKROW,12,14 - MURKROW,12,14 - WURMPLE,9,12 - WURMPLE,9,12 - WURMPLE,9,12 - WURMPLE,9,12 +[047] # Route 7 +Land,25 + 50,SHELLOS,12,15 + 30,BIDOOF,14,17 + 10,MURKROW,12,14 + 10,WURMPLE,9,12 RockSmash - NOSEPASS,13,14 - NOSEPASS,13,14 - GEODUDE,12,15 - GEODUDE,12,15 - GEODUDE,12,15 + 90,NOSEPASS,13,14 + 10,GEODUDE,12,15 #------------------------------- -049 # Rock Cave -25,10,10 -Cave - NOSEPASS,14,15 - NOSEPASS,13,14 - MAGNETON,14,17 - MAGNETON,14,17 - MAGNETON,14,16 - MAGNETON,14,16 - GEODUDE,13,15 - GEODUDE,13,15 - MAWILE,14,16 - MAWILE,14,16 - MAWILE,14,16 - MAWILE,14,16 +[049] # Rock Cave +Cave,10 + 20,MAGNETON,14,16 + 20,MAGNETON,14,17 + 20,NOSEPASS,14,15 + 20,NOSEPASS,13,14 + 10,GEODUDE,13,15 + 10,MAWILE,14,16 #------------------------------- -050 # Rock Cave -25,10,10 -Cave - NOSEPASS,14,15 - NOSEPASS,13,14 - MAGNETON,14,17 - MAGNETON,14,17 - MAGNETON,14,16 - MAGNETON,14,16 - GEODUDE,13,15 - GEODUDE,13,15 - BURMY,14,16 - BURMY,14,16 - BURMY,14,16 - BURMY,14,16 +[050] # Rock Cave +Cave,10 + 20,MAGNETON,14,16 + 20,MAGNETON,14,17 + 20,NOSEPASS,14,15 + 20,NOSEPASS,13,14 + 10,BURMY,14,16 + 10,GEODUDE,13,15 #------------------------------- -051 # Dungeon -25,10,10 -Cave - PICHU,1 - CLEFFA,1 - IGGLYBUFF,1 - IGGLYBUFF,1 - CHINGLING,1 - CHINGLING,1 - RIOLU,1 - RIOLU,1 - TYROGUE,1 - TYROGUE,1 - TYROGUE,1 - TYROGUE,1 +[051] # Dungeon +Cave,10 + 20,CHINGLING,1 + 20,CLEFFA,1 + 20,IGGLYBUFF,1 + 20,PICHU,1 + 10,RIOLU,1 + 10,TYROGUE,1 #------------------------------- -066 # Safari Zone -25,10,10 -Land - NIDORANfE,15,16 - NIDORANmA,15,16 - DODUO,13,15 - DODUO,13,15 - ABRA,12,15 - ABRA,12,15 - TANGELA,14,16 - TANGELA,14,16 - HOPPIP,13,17 - HOPPIP,13,17 - HOPPIP,13,17 - HOPPIP,13,17 +[066] # Safari Zone +Land,25 + 20,ABRA,12,15 + 20,DODUO,13,15 + 20,NIDORANfE,15,16 + 20,NIDORANmA,15,16 + 10,HOPPIP,13,17 + 10,TANGELA,14,16 #------------------------------- -068 # Safari Zone -25,10,10 -Land - RHYHORN,16,18 - EXEGGCUTE,15,18 - VENONAT,15,17 - VENONAT,15,18 - AIPOM,14,17 - GIRAFARIG,16,17 - TAUROS,15,16 - HERACROSS,15,17 - SCYTHER,16 - PINSIR,16 - KANGASKHAN,19 - CHANSEY,17 -Water - PSYDUCK,16,18 - MARILL,15,18 - SLOWPOKE,14,16 - BUIZEL,15,17 - BUIZEL,15,17 +[068] # Safari Zone +Land,25 + 20,EXEGGCUTE,15,18 + 20,RHYHORN,16,18 + 10,AIPOM,14,17 + 10,GIRAFARIG,16,17 + 10,VENONAT,15,17 + 10,VENONAT,15,18 + 5,HERACROSS,15,17 + 5,TAUROS,15,16 + 4,PINSIR,16 + 4,SCYTHER,16 + 1,CHANSEY,17 + 1,KANGASKHAN,19 +Water,10 + 60,PSYDUCK,16,18 + 30,MARILL,15,18 + 5,BUIZEL,15,17 + 5,SLOWPOKE,14,16 OldRod - MAGIKARP,17,21 - MAGIKARP,17,20 + 70,MAGIKARP,17,21 + 30,MAGIKARP,17,20 GoodRod - MAGIKARP,16,20 - FEEBAS,16,20 - POLIWAG,17,18 + 60,MAGIKARP,16,20 + 20,FEEBAS,16,20 + 20,POLIWAG,17,18 SuperRod - GOLDEEN,16,18 - REMORAID,17,19 - CARVANHA,16,17 - FINNEON,15,18 - DRATINI,17 + 40,GOLDEEN,16,18 + 40,REMORAID,17,19 + 15,CARVANHA,16,17 + 4,FINNEON,15,18 + 1,DRATINI,17 #------------------------------- -069 # Route 8 -25,10,10 -Land - ODDISH,15,17 - MAREEP,16,18 - LOTAD,15,17 - LOTAD,15,18 - DITTO,15,17 - DITTO,16,18 - BONSLY,14,17 - BONSLY,14,16 - BONSLY,14,16 - BONSLY,14,16 - BONSLY,15,16 - BONSLY,15 -Water - TENTACOOL,14,19 - MANTYKE,15,16 - REMORAID,14,16 - REMORAID,14,16 - REMORAID,14,16 +[069] # Route 8 +Land,25 + 20,MAREEP,16,18 + 20,ODDISH,15,17 + 13,BONSLY,14,16 + 10,DITTO,15,17 + 10,DITTO,16,18 + 10,LOTAD,15,18 + 10,LOTAD,15,17 + 5,BONSLY,14,17 + 1,BONSLY,15,16 + 1,BONSLY,15 +Water,10 + 60,TENTACOOL,14,19 + 30,MANTYKE,15,16 + 10,REMORAID,14,16 OldRod - MAGIKARP,16,19 - MAGIKARP,16,19 + 100,MAGIKARP,16,19 GoodRod - BARBOACH,17,18 - SHELLDER,16,19 - KRABBY,15,16 + 60,BARBOACH,17,18 + 20,KRABBY,15,16 + 20,SHELLDER,16,19 SuperRod - CHINCHOU,17,19 - QWILFISH,16,19 - CORSOLA,15,18 - STARYU,15,17 - STARYU,15,17 + 40,CHINCHOU,17,19 + 40,QWILFISH,16,19 + 15,CORSOLA,15,18 + 5,STARYU,15,17 #------------------------------- -070 # Underwater -25,10,10 -Land - CLAMPERL,18,20 - SHELLDER,18,20 - CLAMPERL,18,19 - SHELLDER,18,19 - CHINCHOU,17,21 - CHINCHOU,17,21 - CORSOLA,17,20 - CORSOLA,17,20 - RELICANTH,16,19 - RELICANTH,16,19 - RELICANTH,16,19 - RELICANTH,16,19 +[070] # Underwater +Land,25 + 20,CHINCHOU,17,21 + 20,CLAMPERL,18,20 + 20,SHELLDER,18,20 + 10,CLAMPERL,18,19 + 10,CORSOLA,17,20 + 10,RELICANTH,16,19 + 10,SHELLDER,18,19 #------------------------------- -075 # Tiall Region -25,10,10 -Land - RATTATA_1,11,14 - GEODUDE_1,11,14 - SANDSHREW_1,11,14 - VULPIX_1,11,14 - DIGLETT_1,11,14 - MEOWTH_1,11,14 - PIKACHU,11,14 - PIKACHU,11,14 - CUBONE,11,14 - CUBONE,11,14 - CUBONE,11,14 - CUBONE,11,14 +[075] # Tiall Region +Land,25 + 20,GEODUDE_1,11,14 + 20,RATTATA_1,11,14 + 10,CUBONE,11,14 + 10,DIGLETT_1,11,14 + 10,MEOWTH_1,11,14 + 10,PIKACHU,11,14 + 10,SANDSHREW_1,11,14 + 10,VULPIX_1,11,14