From 8975d7a20ca00e5989a8014c0bd1e77312334ba8 Mon Sep 17 00:00:00 2001 From: Maruno17 Date: Sat, 27 Mar 2021 17:36:02 +0000 Subject: [PATCH] Rewrote fading of overworld weather, added fog weather --- .../001_Technical/001_MKXP_Compatibility.rb | 6 + .../001_Game_Temp.rb | 2 - .../003_Game classes/001_Game_Screen.rb | 74 +-- .../003_Game classes/004b_MapFactory.rb | 3 + Data/Scripts/004_Sprites/006_Spriteset_Map.rb | 17 +- .../002_Hardcoded data/014_PBFieldWeather.rb | 3 +- .../002_Hardcoded data/015_Evolution.rb | 2 +- .../Scripts/013_Overworld/002_PField_Field.rb | 4 +- .../013_Overworld/004_PField_Weather.rb | 562 +++++++++++++----- 9 files changed, 458 insertions(+), 215 deletions(-) diff --git a/Data/Scripts/001_Technical/001_MKXP_Compatibility.rb b/Data/Scripts/001_Technical/001_MKXP_Compatibility.rb index 73f36d97d..89e91fac0 100644 --- a/Data/Scripts/001_Technical/001_MKXP_Compatibility.rb +++ b/Data/Scripts/001_Technical/001_MKXP_Compatibility.rb @@ -15,6 +15,12 @@ class Bitmap end end +module Graphics + def self.delta_s + return self.delta.to_f / 1_000_000 + end +end + def pbSetResizeFactor(factor) if !$ResizeInitialized Graphics.resize_screen(Settings::SCREEN_WIDTH, Settings::SCREEN_HEIGHT) diff --git a/Data/Scripts/002_Switches and Variables/001_Game_Temp.rb b/Data/Scripts/002_Switches and Variables/001_Game_Temp.rb index b533d643f..6c0c6fcd2 100644 --- a/Data/Scripts/002_Switches and Variables/001_Game_Temp.rb +++ b/Data/Scripts/002_Switches and Variables/001_Game_Temp.rb @@ -25,7 +25,6 @@ class Game_Temp attr_accessor :fadestate # for sprite hashes attr_accessor :background_bitmap attr_accessor :message_window_showing - attr_accessor :player_transferring attr_accessor :transition_processing attr_accessor :mart_prices #----------------------------------------------------------------------------- @@ -52,7 +51,6 @@ class Game_Temp @fadestate = 0 @background_bitmap = nil @message_window_showing = false - @player_transferring = false @transition_processing = false @mart_prices = {} end diff --git a/Data/Scripts/003_Game classes/001_Game_Screen.rb b/Data/Scripts/003_Game classes/001_Game_Screen.rb index 9dc445b16..e2d1463fc 100644 --- a/Data/Scripts/003_Game classes/001_Game_Screen.rb +++ b/Data/Scripts/003_Game classes/001_Game_Screen.rb @@ -9,39 +9,38 @@ class Game_Screen #----------------------------------------------------------------------------- # * Public Instance Variables #----------------------------------------------------------------------------- - attr_reader :brightness # brightness - attr_reader :tone # color tone - attr_reader :flash_color # flash color - attr_reader :shake # shake positioning - attr_reader :pictures # pictures - attr_reader :weather_type # weather type - attr_reader :weather_max # max number of weather sprites + attr_reader :brightness # brightness + attr_reader :tone # color tone + attr_reader :flash_color # flash color + attr_reader :shake # shake positioning + attr_reader :pictures # pictures + attr_reader :weather_type # weather type + attr_reader :weather_max # max number of weather sprites + attr_accessor :weather_duration # ticks in which the weather should fade in #----------------------------------------------------------------------------- # * Object Initialization #----------------------------------------------------------------------------- def initialize - @brightness = 255 - @fadeout_duration = 0 - @fadein_duration = 0 - @tone = Tone.new(0, 0, 0, 0) - @tone_target = Tone.new(0, 0, 0, 0) - @tone_duration = 0 - @flash_color = Color.new(0, 0, 0, 0) - @flash_duration = 0 - @shake_power = 0 - @shake_speed = 0 - @shake_duration = 0 - @shake_direction = 1 - @shake = 0 - @pictures = [nil] + @brightness = 255 + @fadeout_duration = 0 + @fadein_duration = 0 + @tone = Tone.new(0, 0, 0, 0) + @tone_target = Tone.new(0, 0, 0, 0) + @tone_duration = 0 + @flash_color = Color.new(0, 0, 0, 0) + @flash_duration = 0 + @shake_power = 0 + @shake_speed = 0 + @shake_duration = 0 + @shake_direction = 1 + @shake = 0 + @pictures = [nil] for i in 1..100 @pictures.push(Game_Picture.new(i)) end - @weather_type = 0 - @weather_max = 0.0 - @weather_type_target = 0 - @weather_max_target = 0.0 - @weather_duration = 0 + @weather_type = 0 + @weather_max = 0.0 + @weather_duration = 0 end #----------------------------------------------------------------------------- # * Start Changing Color Tone @@ -82,18 +81,9 @@ class Game_Screen # duration : time #----------------------------------------------------------------------------- def weather(type, power, duration) - @weather_type_target = type - @weather_type = @weather_type_target if @weather_type_target != 0 - if @weather_type_target == 0 - @weather_max_target = 0.0 - else - @weather_max_target = (power + 1) * RPG::Weather::MAX_SPRITES / 10 - end - @weather_duration = duration * Graphics.frame_rate / 20 - if @weather_duration == 0 - @weather_type = @weather_type_target - @weather_max = @weather_max_target - end + @weather_type = type + @weather_max = (power + 1) * RPG::Weather::MAX_SPRITES / 10 + @weather_duration = duration # In 1/20ths of a seconds end #----------------------------------------------------------------------------- # * Frame Update @@ -133,14 +123,6 @@ class Game_Screen @shake_direction = 1 if @shake<-@shake_power*2 @shake_duration -= 1 if @shake_duration>=1 end - if @weather_duration>=1 - d = @weather_duration - @weather_max = (@weather_max*(d-1)+@weather_max_target)/d - @weather_duration -= 1 - if @weather_duration==0 - @weather_type = @weather_type_target - end - end if $game_temp.in_battle for i in 51..100 @pictures[i].update diff --git a/Data/Scripts/003_Game classes/004b_MapFactory.rb b/Data/Scripts/003_Game classes/004b_MapFactory.rb index d5fb61c2a..cb5a19f77 100644 --- a/Data/Scripts/003_Game classes/004b_MapFactory.rb +++ b/Data/Scripts/003_Game classes/004b_MapFactory.rb @@ -94,6 +94,8 @@ class PokemonMapFactory return nil end + # Detects whether the player has moved onto a connected map, and if so, causes + # their transfer to that map. def setCurrentMap return if $game_player.moving? return if $game_map.valid?($game_player.x,$game_player.y) @@ -110,6 +112,7 @@ class PokemonMapFactory pbAutoplayOnTransition $game_map.refresh setMapChanged(oldmap) + $game_screen.weather_duration = 20 end def setMapsInRange diff --git a/Data/Scripts/004_Sprites/006_Spriteset_Map.rb b/Data/Scripts/004_Sprites/006_Spriteset_Map.rb index 38be6f280..3acedfa0a 100644 --- a/Data/Scripts/004_Sprites/006_Spriteset_Map.rb +++ b/Data/Scripts/004_Sprites/006_Spriteset_Map.rb @@ -143,21 +143,12 @@ class Spriteset_Map sprite.update end if self.map!=$game_map - if @weather.max>0 - @weather.max -= 2 - if @weather.max<=0 - @weather.max = 0 - @weather.type = 0 - @weather.ox = 0 - @weather.oy = 0 - end - end + @weather.fade_in(PBFieldWeather::None, 0, 20) else - @weather.type = $game_screen.weather_type - @weather.max = $game_screen.weather_max - @weather.ox = tmox - @weather.oy = tmoy + @weather.fade_in($game_screen.weather_type, $game_screen.weather_max, $game_screen.weather_duration) end + @weather.ox = tmox + @weather.oy = tmoy @weather.update @@viewport1.tone = $game_screen.tone @@viewport3.color = $game_screen.flash_color diff --git a/Data/Scripts/011_Data/002_Hardcoded data/014_PBFieldWeather.rb b/Data/Scripts/011_Data/002_Hardcoded data/014_PBFieldWeather.rb index 40e9c18e6..c97e1fcd6 100644 --- a/Data/Scripts/011_Data/002_Hardcoded data/014_PBFieldWeather.rb +++ b/Data/Scripts/011_Data/002_Hardcoded data/014_PBFieldWeather.rb @@ -8,8 +8,9 @@ begin Sandstorm = 5 HeavyRain = 6 Sun = Sunny = 7 + Fog = 8 - def PBFieldWeather.maxValue; return 7; end + def PBFieldWeather.maxValue; return 8; end end rescue Exception diff --git a/Data/Scripts/011_Data/002_Hardcoded data/015_Evolution.rb b/Data/Scripts/011_Data/002_Hardcoded data/015_Evolution.rb index 7a021b0b6..c6673b28a 100644 --- a/Data/Scripts/011_Data/002_Hardcoded data/015_Evolution.rb +++ b/Data/Scripts/011_Data/002_Hardcoded data/015_Evolution.rb @@ -142,7 +142,7 @@ GameData::Evolution.register({ :level_up_proc => proc { |pkmn, parameter| if pkmn.level >= parameter && $game_screen next [PBFieldWeather::Rain, PBFieldWeather::HeavyRain, - PBFieldWeather::Storm].include?($game_screen.weather_type) + PBFieldWeather::Storm, PBFieldWeather::Fog].include?($game_screen.weather_type) end } }) diff --git a/Data/Scripts/013_Overworld/002_PField_Field.rb b/Data/Scripts/013_Overworld/002_PField_Field.rb index c120d7242..50a490aa4 100644 --- a/Data/Scripts/013_Overworld/002_PField_Field.rb +++ b/Data/Scripts/013_Overworld/002_PField_Field.rb @@ -410,7 +410,7 @@ Events.onMapChanging += proc { |_sender, e| new_map_metadata = GameData::MapMetadata.try_get(new_map_ID) next if new_map_metadata && new_map_metadata.weather end - $game_screen.weather(0, 0, 0) + $game_screen.weather(PBFieldWeather::None, 0, 0) } # Set up various data related to the new map @@ -431,7 +431,7 @@ Events.onMapChange += proc { |_sender, e| next if old_map_metadata && old_map_metadata.weather end new_weather = new_map_metadata.weather - $game_screen.weather(new_weather[0], 8, 20) if rand(100) < new_weather[1] + $game_screen.weather(new_weather[0], 9, 0) if rand(100) < new_weather[1] } Events.onMapSceneChange += proc { |_sender, e| diff --git a/Data/Scripts/013_Overworld/004_PField_Weather.rb b/Data/Scripts/013_Overworld/004_PField_Weather.rb index 5b14e4b38..776a1d141 100644 --- a/Data/Scripts/013_Overworld/004_PField_Weather.rb +++ b/Data/Scripts/013_Overworld/004_PField_Weather.rb @@ -7,21 +7,24 @@ module RPG attr_reader :max attr_reader :ox attr_reader :oy - MAX_SPRITES = 60 + MAX_SPRITES = 60 + FADE_OLD_TILES_START = 0 + FADE_OLD_TILES_END = 1 + FADE_OLD_TONE_START = 0 + FADE_OLD_TONE_END = 2 + FADE_OLD_PARTICLES_START = 1 + FADE_OLD_PARTICLES_END = 3 + FADE_NEW_PARTICLES_START = 2 + FADE_NEW_PARTICLES_END = 4 + FADE_NEW_TONE_START = 3 # Shouldn't be sooner than FADE_OLD_TONE_END + 1 + FADE_NEW_TONE_END = 5 + FADE_NEW_TILES_START = 4 # Shouldn't be sooner than FADE_OLD_TILES_END + FADE_NEW_TILES_END = 5 def initialize(viewport = nil) - @viewport = Viewport.new(0, 0, Graphics.width, Graphics.height) - @viewport.z = viewport.z + 1 - @origViewport = viewport - @type = 0 - @max = 0 - @ox = 0 - @oy = 0 - @tiles_wide = 0 - @tiles_tall = 0 - @sun = 0 - @sunValue = 0 - @time_until_flash = 0 + @viewport = Viewport.new(0, 0, Graphics.width, Graphics.height) + @viewport.z = viewport.z + 1 + @origViewport = viewport # [array of particle bitmaps, array of tile bitmaps, # +x per second (particle), +y per second (particle), +opacity per second (particle), # +x per second (tile), +y per second (tile)] @@ -31,16 +34,32 @@ module RPG @weatherTypes[PBFieldWeather::HeavyRain] = [[], nil, -4800, 4800, 0] @weatherTypes[PBFieldWeather::Storm] = [[], nil, -4800, 4800, 0] @weatherTypes[PBFieldWeather::Snow] = [[], nil, -240, 240, 0] - @weatherTypes[PBFieldWeather::Blizzard] = [[], [], -960, 64, 0, -1440, 720] + @weatherTypes[PBFieldWeather::Blizzard] = [[], [], -960, 256, 0, -1440, 720] @weatherTypes[PBFieldWeather::Sandstorm] = [[], [], -1200, 640, 0, -720, 360] @weatherTypes[PBFieldWeather::Sun] = nil - @sprites = [] - @sprite_lifetimes = [] - @tiles = [] + @weatherTypes[PBFieldWeather::Fog] = [[], [], 0, 0, 0, -32, 0] + @type = 0 + @max = 0 + @ox = 0 + @oy = 0 + @tiles_wide = 0 + @tiles_tall = 0 + @tile_x = 0.0 + @tile_y = 0.0 + @sun_magnitude = 0 # +/- maximum addition to sun tone + @sun_strength = 0 # Current addition to sun tone (0 to @sun_magnitude) + @time_until_flash = 0 + @sprites = [] + @sprite_lifetimes = [] + @tiles = [] + @new_sprites = [] + @new_sprite_lifetimes = [] + @fading = false end def dispose @sprites.each { |sprite| sprite.dispose if sprite } + @new_sprites.each { |sprite| sprite.dispose if sprite } @tiles.each { |sprite| sprite.dispose if sprite } @viewport.dispose @weatherTypes.each do |weather| @@ -50,23 +69,45 @@ module RPG end end + def fade_in(new_type, new_max, duration = 1) + return if @fading + new_max = 0 if new_type == PBFieldWeather::None + return if @type == new_type && @max == new_max + if duration > 0 + @target_type = new_type + @target_max = new_max + prepare_bitmaps(@target_type) + @old_max = @max + @new_max = 0 # Current number of new particles + @old_tone = Tone.new(@viewport.tone.red, @viewport.tone.green, + @viewport.tone.blue, @viewport.tone.gray) + @target_tone = get_weather_tone(@target_type, @target_max) + @fade_time = 0.0 + @time_shift = 0 + if @type == PBFieldWeather::None + @time_shift += 2 # No previous weather to fade out first + elsif !@weatherTypes[@type] || !@weatherTypes[@type][1] || @weatherTypes[@type][1].length == 0 + @time_shift += 1 # No previous tiles to fade out first + end + @fading = true + @new_sprites.each { |sprite| sprite.dispose if sprite } + @new_sprites.clear + ensureSprites + @new_sprites.each_with_index { |sprite, i| set_sprite_bitmap(sprite, i, @target_type) } + else + self.type = new_type + self.max = new_max + end + end + def type=(type) return if @type == type - @type = type - case @type - when PBFieldWeather::None - @sprites.each { |sprite| sprite.dispose if sprite } - @sprites.clear - @tiles.each { |sprite| sprite.dispose if sprite } - @tiles.clear - @tiles_wide = @tiles_tall = 0 - return - when PBFieldWeather::Rain then prepareRainBitmap - when PBFieldWeather::HeavyRain, PBFieldWeather::Storm then prepareStormBitmap - when PBFieldWeather::Snow then prepareSnowBitmaps - when PBFieldWeather::Blizzard then prepareBlizzardBitmaps - when PBFieldWeather::Sandstorm then prepareSandstormBitmaps + if @fading + @max = @target_max + @fading = false end + @type = type + prepare_bitmaps(@type) if @weatherTypes[@type] && @weatherTypes[@type][1] && @weatherTypes[@type][1].length > 0 w = @weatherTypes[@type][1][0].width h = @weatherTypes[@type][1][0].height @@ -76,51 +117,17 @@ module RPG @tiles_wide = @tiles_tall = 0 end ensureSprites - @sprites.each_with_index { |sprite, i| set_sprite_bitmap(sprite, i) } - @tiles.each_with_index { |sprite, i| set_tile_bitmap(sprite, i) } - end - - def set_sprite_bitmap(sprite, index) - return if !sprite - weatherBitmaps = (@weatherTypes[@type]) ? @weatherTypes[@type][0] : nil - if !weatherBitmaps - sprite.bitmap = nil - return - end - case @type - when PBFieldWeather::Rain, PBFieldWeather::HeavyRain, PBFieldWeather::Storm - last_index = weatherBitmaps.length - 1 # Last sprite is splash - if index % 2 == 0 - sprite.bitmap = weatherBitmaps[index % last_index] - else - sprite.bitmap = weatherBitmaps[last_index] - end - else - sprite.bitmap = weatherBitmaps[index % weatherBitmaps.length] - end - end - - def set_tile_bitmap(sprite, index) - return if !sprite - weatherBitmaps = (@weatherTypes[@type]) ? @weatherTypes[@type][1] : nil - if !weatherBitmaps || weatherBitmaps.length == 0 - sprite.bitmap = nil - return - end - sprite.bitmap = weatherBitmaps[index % weatherBitmaps.length] - reset_tile_position(sprite, index) + @sprites.each_with_index { |sprite, i| set_sprite_bitmap(sprite, i, @type) } + ensureTiles + @tiles.each_with_index { |sprite, i| set_tile_bitmap(sprite, i, @type) } end def max=(value) return if @max == value @max = value.clamp(0, MAX_SPRITES) - if @max == 0 - @sprites.each { |sprite| sprite.dispose if sprite } - @sprites.clear - @tiles.each { |sprite| sprite.dispose if sprite } - @tiles.clear - else - @sprites.each_with_index { |sprite, i| sprite.visible = (i <= @max) if sprite } + ensureSprites + for i in 0...MAX_SPRITES + @sprites[i].visible = (i < @max) if @sprites[i] end end @@ -128,6 +135,7 @@ module RPG return if value == @ox @ox = value @sprites.each { |sprite| sprite.ox = @ox if sprite } + @new_sprites.each { |sprite| sprite.ox = @ox if sprite } @tiles.each { |sprite| sprite.ox = @ox if sprite } end @@ -135,10 +143,42 @@ module RPG return if value == @oy @oy = value @sprites.each { |sprite| sprite.oy = @oy if sprite } + @new_sprites.each { |sprite| sprite.oy = @oy if sprite } @tiles.each { |sprite| sprite.oy = @oy if sprite } end - def prepareRainBitmap + def get_weather_tone(weather_type, maximum) + case weather_type + when PBFieldWeather::Rain + return Tone.new(-maximum * 3 / 4, -maximum * 3 / 4, -maximum * 3 / 4, 10) + when PBFieldWeather::HeavyRain + return Tone.new(-maximum * 6 / 4, -maximum * 6 / 4, -maximum * 6 / 4, 20) + when PBFieldWeather::Storm + return Tone.new(-maximum * 6 / 4, -maximum * 6 / 4, -maximum * 6 / 4, 20) + when PBFieldWeather::Snow + return Tone.new( maximum / 2, maximum / 2, maximum / 2, 0) + when PBFieldWeather::Blizzard + return Tone.new( maximum * 3 / 4, maximum * 3 / 4, maximum * 3 / 4, 0) + when PBFieldWeather::Sandstorm + return Tone.new( maximum / 2, 0, -maximum / 2, 0) + when PBFieldWeather::Sun + return Tone.new(64, 64, 32, 0) + end + return Tone.new(0, 0, 0, 0) + end + + def prepare_bitmaps(new_type) + case new_type + when PBFieldWeather::Rain then prepareRainBitmaps + when PBFieldWeather::HeavyRain, PBFieldWeather::Storm then prepareStormBitmaps + when PBFieldWeather::Snow then prepareSnowBitmaps + when PBFieldWeather::Blizzard then prepareBlizzardBitmaps + when PBFieldWeather::Sandstorm then prepareSandstormBitmaps + when PBFieldWeather::Fog then prepareFogBitmaps + end + end + + def prepareRainBitmaps rain1 = RPG::Cache.load_bitmap("Graphics/Weather/", "rain_1") rain2 = RPG::Cache.load_bitmap("Graphics/Weather/", "rain_2") rain3 = RPG::Cache.load_bitmap("Graphics/Weather/", "rain_3") @@ -146,7 +186,7 @@ module RPG @weatherTypes[PBFieldWeather::Rain][0] = [rain1, rain2, rain3, rain4] end - def prepareStormBitmap + def prepareStormBitmaps storm1 = RPG::Cache.load_bitmap("Graphics/Weather/", "storm_1") storm2 = RPG::Cache.load_bitmap("Graphics/Weather/", "storm_2") storm3 = RPG::Cache.load_bitmap("Graphics/Weather/", "storm_3") @@ -182,8 +222,14 @@ module RPG @weatherTypes[PBFieldWeather::Sandstorm][1] = [sandstorm_tile] end + def prepareFogBitmaps + fog_tile = RPG::Cache.load_bitmap("Graphics/Weather/", "fog_tile") + @weatherTypes[PBFieldWeather::Fog][1] = [fog_tile] + end + def ensureSprites - if @sprites.length < MAX_SPRITES + if @sprites.length < MAX_SPRITES && @weatherTypes[@type] && + @weatherTypes[@type][0] && @weatherTypes[@type][0].length > 0 for i in 0...MAX_SPRITES if !@sprites[i] sprite = Sprite.new(@origViewport) @@ -193,138 +239,354 @@ module RPG sprite.opacity = 0 @sprites[i] = sprite end - @sprites[i].visible = (i <= @max) + @sprites[i].visible = (i < @max) @sprite_lifetimes[i] = 0 end end - if @tiles.length < @tiles_wide * @tiles_tall - for i in 0...(@tiles_wide * @tiles_tall) - if !@tiles[i] + if @fading && @new_sprites.length < MAX_SPRITES && @weatherTypes[@target_type] && + @weatherTypes[@target_type][0] && @weatherTypes[@target_type][0].length > 0 + for i in 0...MAX_SPRITES + if !@new_sprites[i] sprite = Sprite.new(@origViewport) sprite.z = 1000 sprite.ox = @ox sprite.oy = @oy sprite.opacity = 0 - @tiles[i] = sprite + @new_sprites[i] = sprite end - @tiles[i].visible = true + @new_sprites[i].visible = (i < @new_max) + @new_sprite_lifetimes[i] = 0 end end end - def reset_sprite_position(sprite, index) - if [PBFieldWeather::Rain, PBFieldWeather::HeavyRain, PBFieldWeather::Storm].include?(@type) && index % 2 != 0 # Splash + def ensureTiles + return if @tiles.length >= @tiles_wide * @tiles_tall + for i in 0...(@tiles_wide * @tiles_tall) + if !@tiles[i] + sprite = Sprite.new(@origViewport) + sprite.z = 1000 + sprite.ox = @ox + sprite.oy = @oy + sprite.opacity = 0 + @tiles[i] = sprite + end + @tiles[i].visible = true + end + end + + def set_sprite_bitmap(sprite, index, weather_type) + return if !sprite + weatherBitmaps = (@weatherTypes[weather_type]) ? @weatherTypes[weather_type][0] : nil + if !weatherBitmaps || weatherBitmaps.length == 0 + sprite.bitmap = nil + return + end + case weather_type + when PBFieldWeather::Rain, PBFieldWeather::HeavyRain, PBFieldWeather::Storm + last_index = weatherBitmaps.length - 1 # Last sprite is splash + if (index % 2) == 0 + sprite.bitmap = weatherBitmaps[index % last_index] + else + sprite.bitmap = weatherBitmaps[last_index] + end + else + sprite.bitmap = weatherBitmaps[index % weatherBitmaps.length] + end + end + + def set_tile_bitmap(sprite, index, weather_type) + return if !sprite || !weather_type + weatherBitmaps = (@weatherTypes[weather_type]) ? @weatherTypes[weather_type][1] : nil + if weatherBitmaps && weatherBitmaps.length > 0 + sprite.bitmap = weatherBitmaps[index % weatherBitmaps.length] + else + sprite.bitmap = nil + end + end + + def reset_sprite_position(sprite, index, is_new_sprite = false) + weather_type = (is_new_sprite) ? @target_type : @type + lifetimes = (is_new_sprite) ? @new_sprite_lifetimes : @sprite_lifetimes + if index < (is_new_sprite ? @new_max : @max) + sprite.visible = true + else + sprite.visible = false + lifetimes[index] = 0 + return + end + if [PBFieldWeather::Rain, PBFieldWeather::HeavyRain, + PBFieldWeather::Storm].include?(weather_type) && (index % 2) != 0 # Splash sprite.x = @ox - sprite.bitmap.width + rand(Graphics.width + sprite.bitmap.width * 2) sprite.y = @oy - sprite.bitmap.height + rand(Graphics.height + sprite.bitmap.height * 2) - @sprite_lifetimes[index] = (30 + rand(20)) * 10_000 # 0.3-0.5 seconds + lifetimes[index] = (30 + rand(20)) * 0.01 # 0.3-0.5 seconds else - gradient = @weatherTypes[@type][2].to_f / @weatherTypes[@type][3] - sprite.x = @ox - sprite.bitmap.width + rand(Graphics.width + sprite.bitmap.width * 2 - gradient * Graphics.height) - sprite.y = @oy - sprite.bitmap.height - rand(Graphics.height) - @sprite_lifetimes[index] = 1_000_000 * (@oy - sprite.y + rand(Graphics.height * 8 / 5)) / @weatherTypes[@type][3] + gradient = @weatherTypes[weather_type][2].to_f / @weatherTypes[weather_type][3] + if gradient.abs >= 1 + # Position sprite to the right of the screen + sprite.x = @ox + Graphics.width + rand(Graphics.width) + sprite.y = @oy + Graphics.height - rand(Graphics.height + sprite.bitmap.height - Graphics.width / gradient) + distance_to_cover = sprite.x - @ox - Graphics.width / 2 + sprite.bitmap.width + rand(Graphics.width * 8 / 5) + lifetimes[index] = (distance_to_cover.to_f / @weatherTypes[weather_type][2]).abs + else + # Position sprite to the top of the screen + sprite.x = @ox - sprite.bitmap.width + rand(Graphics.width + sprite.bitmap.width - gradient * Graphics.height) + sprite.y = @oy - sprite.bitmap.height - rand(Graphics.height) + distance_to_cover = @oy - sprite.y + Graphics.height / 2 + rand(Graphics.height * 8 / 5) + lifetimes[index] = (distance_to_cover.to_f / @weatherTypes[weather_type][3]).abs + end end sprite.opacity = 255 end - def update_sprite_position(sprite, index) - return if !sprite - delta_t = Graphics.delta - if @sprite_lifetimes[index] > 0 - @sprite_lifetimes[index] -= delta_t - if @sprite_lifetimes[index] <= 0 - reset_sprite_position(sprite, index) + def update_sprite_position(sprite, index, is_new_sprite = false) + return if !sprite || !sprite.bitmap || !sprite.visible + delta_t = Graphics.delta_s + lifetimes = (is_new_sprite) ? @new_sprite_lifetimes : @sprite_lifetimes + if lifetimes[index] >= 0 + lifetimes[index] -= delta_t + if lifetimes[index] <= 0 + reset_sprite_position(sprite, index, is_new_sprite) return end end - if [PBFieldWeather::Rain, PBFieldWeather::HeavyRain, PBFieldWeather::Storm].include?(@type) && index % 2 != 0 # Splash - sprite.visible = (@sprite_lifetimes[index] < 200_000) # 0.2 seconds + # Determine which weather type this sprite is representing + weather_type = (is_new_sprite) ? @target_type : @type + # Update visibility/position/opacity of sprite + if [PBFieldWeather::Rain, PBFieldWeather::HeavyRain, + PBFieldWeather::Storm].include?(weather_type) && (index % 2) != 0 # Splash + sprite.opacity = (lifetimes[index] < 0.2) ? 255 : 0 # 0.2 seconds else - sprite.x += @weatherTypes[@type][2] * delta_t / 1_000_000 - sprite.y += @weatherTypes[@type][3] * delta_t / 1_000_000 - if @type == PBFieldWeather::Snow || @type == PBFieldWeather::Blizzard - sprite.x -= (4 * (sprite.y - @oy)) / Graphics.height - sprite.x -= [2, 1, 0, -1][rand(4)] - sprite.y += index % 6 + dist_x = @weatherTypes[weather_type][2] * delta_t + dist_y = @weatherTypes[weather_type][3] * delta_t + sprite.x += dist_x + sprite.y += dist_y + if weather_type == PBFieldWeather::Snow + sprite.x += dist_x * (sprite.y - @oy) / (Graphics.height * 3) # Faster when further down screen + sprite.x += [2, 1, 0, -1][rand(4)] * dist_x / 8 # Random movement + sprite.y += [2, 1, 1, 0, 0, -1][index % 6] * dist_y / 10 # Variety + end + sprite.opacity += @weatherTypes[weather_type][4] * delta_t + x = sprite.x - @ox + y = sprite.y - @oy + # Check if sprite is off-screen; if so, reset it + if sprite.opacity < 64 || x < -sprite.bitmap.width || y > Graphics.height + reset_sprite_position(sprite, index, is_new_sprite) end - sprite.opacity += @weatherTypes[@type][4] * delta_t / 1_000_000 - end - x = sprite.x - @ox - y = sprite.y - @oy - # Check if sprite is off-screen; if so, reset it - if sprite.opacity < 64 || x < -sprite.bitmap.width || y > Graphics.height - reset_sprite_position(sprite, index) end end - def reset_tile_position(sprite, index) - sprite.x = @ox + (index % @tiles_wide) * sprite.bitmap.width - sprite.y = @oy + (index / @tiles_wide) * sprite.bitmap.height + def recalculate_tile_positions + delta_t = Graphics.delta_s + weather_type = @type + if @fading && @fade_time >= [FADE_OLD_TONE_END - @time_shift, 0].max + weather_type = @target_type + end + @tile_x += @weatherTypes[weather_type][5] * delta_t + @tile_y += @weatherTypes[weather_type][6] * delta_t + if @tile_x < -@tiles_wide * @weatherTypes[weather_type][1][0].width + @tile_x += @tiles_wide * @weatherTypes[weather_type][1][0].width + end + if @tile_y > @tiles_tall * @weatherTypes[weather_type][1][0].height + @tile_y -= @tiles_tall * @weatherTypes[weather_type][1][0].height + end end def update_tile_position(sprite, index) - return if !sprite || !sprite.bitmap - delta_t = Graphics.delta - if @tiles_wide > 0 && @tiles_tall > 0 - sprite.x += @weatherTypes[@type][5] * delta_t / 1_000_000 - sprite.y += @weatherTypes[@type][6] * delta_t / 1_000_000 - sprite.x += @tiles_wide * sprite.bitmap.width if sprite.x - @ox + sprite.bitmap.width < 0 - sprite.y -= @tiles_tall * sprite.bitmap.height if sprite.y - @oy > Graphics.height - sprite.visible = true - sprite.opacity = 255 + return if !sprite || !sprite.bitmap || !sprite.visible + weather_type = @type + if @fading && @fade_time >= [FADE_OLD_TONE_END - @time_shift, 0].max + weather_type = @target_type + end + sprite.x = @ox + @tile_x + (index % @tiles_wide) * sprite.bitmap.width + sprite.y = @oy + @tile_y + (index / @tiles_wide) * sprite.bitmap.height + sprite.x += @tiles_wide * sprite.bitmap.width if sprite.x - @ox < -sprite.bitmap.width + sprite.y -= @tiles_tall * sprite.bitmap.height if sprite.y - @oy > Graphics.height + sprite.visible = true + if @fading && @type != @target_type + if @fade_time >= FADE_OLD_TILES_START && @fade_time < FADE_OLD_TILES_END + if @time_shift == 0 # There were old tiles to fade out + fraction = (@fade_time - [FADE_OLD_TILES_START - @time_shift, 0].max) / (FADE_OLD_TILES_END - FADE_OLD_TILES_START) + sprite.opacity = 255 * (1 - fraction) + end + elsif @fade_time >= [FADE_NEW_TILES_START - @time_shift, 0].max && + @fade_time < [FADE_NEW_TILES_END - @time_shift, 0].max + fraction = (@fade_time - [FADE_NEW_TILES_START - @time_shift, 0].max) / (FADE_NEW_TILES_END - FADE_NEW_TILES_START) + sprite.opacity = 255 * fraction + else + sprite.opacity = 0 + end else - sprite.visible = false + sprite.opacity = (@max > 0) ? 255 : 0 end end # Set tone of viewport (general screen brightening/darkening) def update_screen_tone - # @max is (power+1)*MAX_SPRITES/10, where power is between 1 and 9 - case @type - when PBFieldWeather::None - @viewport.tone.set(0, 0, 0, 0) - when PBFieldWeather::Rain - @viewport.tone.set(-@max * 3 / 4, -@max * 3 / 4, -@max * 3 / 4, 10) - when PBFieldWeather::HeavyRain - @viewport.tone.set(-@max * 6 / 4, -@max * 6 / 4, -@max * 6 / 4, 20) - when PBFieldWeather::Storm - @viewport.tone.set(-@max * 6 / 4, -@max * 6 / 4, -@max * 6 / 4, 20) - when PBFieldWeather::Snow - @viewport.tone.set( @max / 2, @max / 2, @max / 2, 0) - when PBFieldWeather::Blizzard - @viewport.tone.set( @max * 3 / 4, @max * 3 / 4, max * 3 / 4, 0) - when PBFieldWeather::Sandstorm - @viewport.tone.set( @max / 2, 0, -@max / 2, 0) - when PBFieldWeather::Sun - @sun = @max if @sun != @max && @sun != -@max - @sun *= -1 if (@sun > 0 && @sunValue > @max) || (@sun < 0 && @sunValue < 0) - @sunValue += @sun.to_f * Graphics.delta / 400_000 # 0.4 seconds - @viewport.tone.set(@sunValue + 63, @sunValue + 63, @sunValue / 2 + 31, 0) + weather_type = @type + weather_max = @max + fraction = 1 + tone_red = 0 + tone_green = 0 + tone_blue = 0 + tone_gray = 0 + # Get base tone + if @fading + if @type == @target_type # Just changing max + if @fade_time >= [FADE_NEW_TONE_START - @time_shift, 0].max && + @fade_time < [FADE_NEW_TONE_END - @time_shift, 0].max + weather_max = @target_max + fract = (@fade_time - [FADE_NEW_TONE_START - @time_shift, 0].max) / (FADE_NEW_TONE_END - FADE_NEW_TONE_START) + tone_red = @target_tone.red + (1 - fract) * (@old_tone.red - @target_tone.red) + tone_green = @target_tone.green + (1 - fract) * (@old_tone.green - @target_tone.green) + tone_blue = @target_tone.blue + (1 - fract) * (@old_tone.blue - @target_tone.blue) + tone_gray = @target_tone.gray + (1 - fract) * (@old_tone.gray - @target_tone.gray) + else + tone_red = @viewport.tone.red + tone_green = @viewport.tone.green + tone_blue = @viewport.tone.blue + tone_gray = @viewport.tone.gray + end + elsif @time_shift < 2 && @fade_time >= FADE_OLD_TONE_START && @fade_time < FADE_OLD_TONE_END + weather_max = @old_max + fraction = ((@fade_time - FADE_OLD_TONE_START) / (FADE_OLD_TONE_END - FADE_OLD_TONE_START)).clamp(0, 1) + fraction = 1 - fraction + tone_red = @old_tone.red + tone_green = @old_tone.green + tone_blue = @old_tone.blue + tone_gray = @old_tone.gray + elsif @fade_time >= [FADE_NEW_TONE_START - @time_shift, 0].max + weather_type = @target_type + weather_max = @target_max + fraction = ((@fade_time - [FADE_NEW_TONE_START - @time_shift, 0].max) / (FADE_NEW_TONE_END - FADE_NEW_TONE_START)).clamp(0, 1) + tone_red = @target_tone.red + tone_green = @target_tone.green + tone_blue = @target_tone.blue + tone_gray = @target_tone.gray + end + else + base_tone = get_weather_tone(weather_type, weather_max) + tone_red = base_tone.red + tone_green = base_tone.green + tone_blue = base_tone.blue + tone_gray = base_tone.gray + end + # Modify base tone + if weather_type == PBFieldWeather::Sun + @sun_magnitude = weather_max if @sun_magnitude != weather_max && @sun_magnitude != -weather_max + @sun_magnitude *= -1 if (@sun_magnitude > 0 && @sun_strength > @sun_magnitude) || + (@sun_magnitude < 0 && @sun_strength < 0) + @sun_strength += @sun_magnitude.to_f * Graphics.delta_s * 0.4 # 0.4 seconds + tone_red += @sun_strength + tone_green += @sun_strength + tone_blue += @sun_strength / 2 + end + # Apply screen tone + @viewport.tone.set(tone_red * fraction, tone_green * fraction, + tone_blue * fraction, tone_gray * fraction) + end + + def update_fading + return if !@fading + old_fade_time = @fade_time + @fade_time += Graphics.delta_s + # Change tile bitmaps + if @type != @target_type + tile_change_threshold = [FADE_OLD_TONE_END - @time_shift, 0].max + if old_fade_time <= tile_change_threshold && @fade_time > tile_change_threshold + @tile_x = @tile_y = 0.0 + if @weatherTypes[@target_type] && @weatherTypes[@target_type][1] && @weatherTypes[@target_type][1].length > 0 + w = @weatherTypes[@target_type][1][0].width + h = @weatherTypes[@target_type][1][0].height + @tiles_wide = (Graphics.width.to_f / w).ceil + 1 + @tiles_tall = (Graphics.height.to_f / h).ceil + 1 + ensureTiles + @tiles.each_with_index { |sprite, i| set_tile_bitmap(sprite, i, @target_type) } + else + @tiles_wide = @tiles_tall = 0 + end + end + end + # Reduce the number of old weather particles + if @max > 0 && @fade_time >= [FADE_OLD_PARTICLES_START - @time_shift, 0].max + fraction = (@fade_time - [FADE_OLD_PARTICLES_START - @time_shift, 0].max) / (FADE_OLD_PARTICLES_END - FADE_OLD_PARTICLES_START) + @max = @old_max * (1 - fraction) + # NOTE: Sprite visibilities aren't updated here; a sprite is allowed to + # die off naturally in def reset_sprite_position. + end + # Increase the number of new weather particles + if @new_max < @target_max && @fade_time >= [FADE_NEW_PARTICLES_START - @time_shift, 0].max + fraction = (@fade_time - [FADE_NEW_PARTICLES_START - @time_shift, 0].max) / (FADE_NEW_PARTICLES_END - FADE_NEW_PARTICLES_START) + @new_max = (@target_max * fraction).floor + @new_sprites.each_with_index { |sprite, i| sprite.visible = (i < @new_max) if sprite } + end + # End fading + if @fade_time >= ((@target_type == PBFieldWeather::None) ? FADE_OLD_PARTICLES_END : FADE_NEW_TILES_END) - @time_shift + if !@sprites.any? { |sprite| sprite.visible } + @type = @target_type + @max = @target_max + @target_type = nil + @target_max = nil + @old_max = nil + @new_max = nil + @old_tone = nil + @target_tone = nil + @fade_time = 0.0 + @time_shift = 0 + @sprites.each { |sprite| sprite.dispose if sprite } + @sprites = @new_sprites + @new_sprites = [] + @sprite_lifetimes = @new_sprite_lifetimes + @new_sprite_lifetimes = [] + @fading = false + end end end def update - # @max is (power+1)*MAX_SPRITES/10, where power is between 1 and 9 + update_fading update_screen_tone # Storm flashes - if @type == PBFieldWeather::Storm + if @type == PBFieldWeather::Storm && !@fading if @time_until_flash > 0 - @time_until_flash -= Graphics.delta + @time_until_flash -= Graphics.delta_s if @time_until_flash <= 0 @viewport.flash(Color.new(255, 255, 255, 230), (2 + rand(3)) * 20) end end if @time_until_flash <= 0 - @time_until_flash = (1 + rand(12)) * 500_000 # 0.5-6 seconds + @time_until_flash = (1 + rand(12)) * 0.5 # 0.5-6 seconds end end @viewport.update # Update weather particles (raindrops, snowflakes, etc.) - if @weatherTypes[@type] + if @weatherTypes[@type] && @weatherTypes[@type][0] && @weatherTypes[@type][0].length > 0 ensureSprites - for i in 0...@max - update_sprite_position(@sprites[i], i) + for i in 0...MAX_SPRITES + update_sprite_position(@sprites[i], i, false) end + elsif @sprites.length > 0 + @sprites.each { |sprite| sprite.dispose if sprite } + @sprites.clear + end + # Update new weather particles (while fading in only) + if @fading && @weatherTypes[@target_type] && @weatherTypes[@target_type][0] && @weatherTypes[@target_type][0].length > 0 + ensureSprites + for i in 0...MAX_SPRITES + update_sprite_position(@new_sprites[i], i, true) + end + elsif @new_sprites.length > 0 + @new_sprites.each { |sprite| sprite.dispose if sprite } + @new_sprites.clear + end + # Update weather tiles (sandstorm/blizzard tiled overlay) + if @tiles_wide > 0 && @tiles_tall > 0 + ensureTiles + recalculate_tile_positions @tiles.each_with_index { |sprite, i| update_tile_position(sprite, i) } + elsif @tiles.length > 0 + @tiles.each { |sprite| sprite.dispose if sprite } + @tiles.clear end end end