Hi @Christopho! I need some help. I have a problem with my rain script. I don't kow if this is a bug or something I did wrong. I think the problem started when I began refactoring my code, but I am not sure. My (unfinished) rain script is this one:
-- Rain manager script.
--[[
To add this script to your game, call from game_manager script:
require("scripts/weather/rain_manager")
The functions here defined are:
game:get_rain_type(world)
game:set_rain_type(world, rain_type)
Rain types: nil (no rain), "rain", "storm".
--]]
-- This script requires the multi_event script:
require("scripts/multi_events")
local rain_manager = {}
local game_meta = sol.main.get_metatable("game")
local map_meta = sol.main.get_metatable("map")
-- Default settings. Change these for testing.
local rain_enabled = true -- Do not change this property, unless you are testing.
local lightning_enabled = false
local rain_speed = 100 -- Default drop speed 100.
local drop_max_distance = 300 -- Max possible distance for drop movements.
local drop_delay = 10 -- Delay between drops, in milliseconds.
local drop_sprite_id = "test/rain"
-- Initialize rain on maps when necessary.
game_meta:register_event("on_map_changed", function()
if self ~= nil then
local map = self:get_map()
rain_manager:update_rain(map)
end
end)
-- Get the raining state for a given world.
function game_meta:get_rain_type(world)
local rain_type = self:get_value("rain_state_" .. world)
return rain_enabled and rain_type
end
-- Set the raining state for a given world.
function game_meta:set_rain_type(world, rain_type)
-- Update savegame variable.
self:set_value("rain_state_" .. world, rain_type)
-- Check if rain is necessary: if we are in that world and rain is needed.
local current_world = self:get_map():get_world()
local rain_needed = (current_world == world) and rain_enabled and rain_type
if (not rain_needed) then return end -- Do nothing if rain is not needed!
-- We need to start the rain in the current map.
local map = self:get_map()
rain_manager:start_rain(map)
end
-- Create rain if necessary when entering a new map.
function rain_manager:update_rain(map)
-- Get rain state in this world.
local world = map:get_world()
local rain_type = map:get_game():get_rain_type(world)
-- Start rain if necessary.
if rain_type == "rain" then
self:start_rain(map)
elseif rain_type == "storm" then
self:start_storm(map)
end
end
-- Define function to create splash effects.
-- If no parameters x, y are given, the position is random.
local function create_drop_splash(map, x, y)
local max_layer = map:get_max_layer()
local min_layer = map:get_min_layer()
local camera = map:get_camera()
local cx, cy, cw, ch = camera:get_bounding_box()
local drop_properties = {direction = 0, x = 0, y = 0, layer = max_layer,
width = 16, height = 16, sprite = drop_sprite_id}
-- Initialize parameters.
local x = x or cx + cw * math.random()
local y = y or cy + ch * math.random()
local layer = max_layer
while map:get_ground(x,y,layer) == "empty" and layer > min_layer do
layer = layer - 1 -- Draw the splash at the lower layer we can.
end
-- Do not draw splash over some bad grounds: "hole" and "lava".
local ground = map:get_ground(x, y, layer)
if ground ~= "hole" and ground ~= "lava" then
drop_properties.x = x
drop_properties.y = y
drop_properties.layer = layer
local drop_splash = map:create_custom_entity(drop_properties)
assert(drop_splash ~= nil)
local splash_sprite = drop_splash:get_sprite()
splash_sprite:set_animation("drop_splash")
splash_sprite:set_direction(0)
function splash_sprite:on_animation_finished() drop_splash:remove() end
end
end
-- Define function to create drops.
-- If no parameters x, y are given, the position is random.
local function create_drop(map, x, y)
local max_layer = map:get_max_layer()
local min_layer = map:get_min_layer()
local camera = map:get_camera()
local cx, cy, cw, ch = camera:get_bounding_box()
local drop_properties = {direction = 0, x = 0, y = 0, layer = max_layer,
width = 16, height = 16, sprite = drop_sprite_id}
-- Initialize parameters.
drop_properties.x = x or cx + cw * math.random() + 30
drop_properties.y = y or cy + ch * math.random() - 100
drop_properties.layer = max_layer
local drop = map:create_custom_entity(drop_properties)
local m = sol.movement.create("straight")
m:set_angle(7 * math.pi / 5)
m:set_speed(rain_speed)
local random_max_distance = math.random(1, drop_max_distance)
m:set_max_distance(random_max_distance)
m:set_ignore_obstacles()
function m:on_finished() drop:remove() end
function m:on_obstacle_reached() drop:remove() end
function drop:on_removed()
local x, y = drop:get_position()
create_drop_splash(map, x, y)
end
m:start(drop)
end
-- Start rain in the current map.
function rain_manager:start_rain(map)
local max_layer = map:get_max_layer()
local min_layer = map:get_min_layer()
local camera = map:get_camera()
local drop_properties = {direction = 0, x = 0, y = 0, layer = max_layer,
width = 16, height = 16, sprite = drop_sprite_id}
-- Initialize random seed for positions.
math.randomseed(os.time())
-- Start timer to draw rain drops.
sol.timer.start(map, drop_delay, function()
-- Create drops on random positions.
create_drop(map)
-- Repeat loop.
return true
end)
end
-- Stop rain in the current map.
function rain_manager:stop_rain(map)
end
-- Return rain manager.
return rain_manager
The problem appears when leaving a map with rain to another (where there is no rain, but this is probably not important). The bug appears in the console (no crash) at lines 92-93, because the entity "drop_splash" does not exist in that moment. I know that this can be fixed with an extra condition "if drop_splash ~= nil then blablabla end", but I'd like to know why does this happen.
I had the same problem at lines 31-32, because the "self" variable does not exist sometimes, and the condition "if self ~= nil then" fixed this. Why do not these variables exist when these events are called? Is this a bug?
Thanks in advance for the help.