Script Information :
- Fog Effect : Display and move a surface, creating a fog moving effect.
This script is heavily inspired by the Day/Night / Fog effect tuto made by Christopho (most of the code is from the tutorial.), but this is more generalized since it use metatable (so it avoid copies over maps script.).
How it works ?
- place the script in quest_manager (or other file that regroup all metatable)
- copy/paste the script in a function that hold the map metatable.
- Place your fogs png in /sprites/fogs
- The fog surface should be higher or equal to 320x240 (or else it would do strange thing where you will need to duplicate the fog, which will result in higher map loading time and framerate issue)
- In your map script, in on_started(), add
function map:on_started(destination)
map:display_fog("filename", speed, angle, opacity)
end
where :
filename must be a string pointing to an existing file in /sprites/fog
speed a number (0 = immobile, higher value = faster)
angle a number between 0 and 7 (0 = right, 7 = south-east
opacity a number between 0 and 255 (0 = transparent, 255 = opaque)
- A savegame value that check if there is any fog graphic. If yes, it is drawn.
Problems :
- If you use map:on_draw(surface) in your map script, the function in the metatable is overwritten, but, this script is temporary before solarus allows shader
What's next :
- add a parameter that analyse the size of the map, if higher then 320x240, automatically multiply the surface to fit the map.
- add a boolean, if true, the fog will slightly follow the camera movement
- fade in/fade out with user custom timer. (Useful for sunrays-like fog)
function map_metatable:display_fog(fog, speed, angle, opacity)
local fog = fog or nil
local speed = speed or 1
local angle = angle or 0
local opacity = opacity or 16
if type(fog) == "string" then
self.fog = sol.surface.create("fogs/"..fog..".png")
self.fog:set_opacity(opacity)
self.fog_size_x, self.fog_size_y = self.fog:get_size()
self.fog_m = sol.movement.create("straight")
function restart_overlay_movement()
self.fog_m:set_speed(speed)
self.fog_m:set_max_distance(self.fog_size_x)
self.fog_m:set_angle(angle * math.pi / 4)
self.fog_m:start(self.fog, function()
restart_overlay_movement()
self.fog:set_xy(0,0)
end)
end
restart_overlay_movement()
self:get_game():set_value("current_fog", fog)
end
end
function map_metatable:get_current_fog()
return self:get_game():get_value("current_fog")
end
function map_metatable:on_draw(dst_surface)
local scr_x, scr_y = dst_surface:get_size()
if self:get_current_fog() ~= nil then
local camera_x, camera_y = self:get_camera_position()
local overlay_width, overlay_height = self.fog:get_size()
local x, y = camera_x, camera_y
x, y = -math.floor(x), -math.floor(y)
x = x % overlay_width - 4 * overlay_width
y = y % overlay_height - 4 * overlay_height
local dst_y = y
while dst_y < scr_y + overlay_height do
local dst_x = x
while dst_x < scr_x + overlay_width do
self.fog:draw(dst_surface, dst_x, dst_y)
dst_x = dst_x + overlay_width
end
dst_y = dst_y + overlay_height
end
end
end
function map_metatable:on_finished()
self:get_game():set_value("current_fog", nil)
end
This is an example of what you can do.
(http://i.imgur.com/cciAfju.png)
Script 2: Map name
- Display a map name. Simple.
- Can be converted into a hud menu and be a part of the hud so it is started when the hud starts. (PM if interested.)
How it works
-Plug and play, the only thing you need is adding things in strings.dat and call a function in map:on_started(destination)
-You need the map_name font, a utf-8 png (file at the end of the post.) that you need to declare in project.db
text{ key = "map.gameplay.boss_name.0", value = "- Boss Name - $ Some Text about him" }
text{ key = "map.gameplay.map_name.graveyard", value = "Map Name" }
- Careful, do not remove the "$" in boss_name if you want to display a quick info (like OOT boss presentation), it automatically skip a line.
and in your map script :
The script can be called anywhere in the map script.
map:show_map_name("graveyard")
For a map name, example, we are in a dungeon and I want to display a map name only when the destination is the entrance, my destination name is "from_outside" in the map editor.
function map:on_started(destination)
if destination == from_outside then
self:show_map_name("graveyard") -- self is the map.
end
end
for a boss name, it use the same thing, but it use an extra parameter, you can call it from anywhere (after a cutscene for example), the only limit is that it need the map object.
map:show_map_name("0", "boss_name")
-- 0 = the boss_name pointer in string.dat
The script :
Insert it in quest_manager o in another script that manage metatable.
function map_metatable:show_map_name(map_name, display_extra)
local map_name = map_name or nil
local display_extra = display_extra or nil
if type(map_name) == "string" then
self.map_name = sol.text_surface.create{
horizontal_alignment = "center",
vertical_alignment = "middle",
font = "map_name",
}
self.boss_presentation_text = sol.text_surface.create{
horizontal_alignment = "center",
vertical_alignment = "middle",
font = "map_name",
}
if (type(display_extra) == "string" and display_extra ~= "boss_name") or type(display_extra) == "nil" then
self.map_name:set_text_key("map.gameplay.map_name."..map_name)
self.mx, self.my = self.map_name:get_size()
self.map_name_surface = sol.surface.create(self.mx * 2, self.my)
self.map_name:draw(self.map_name_surface, self.mx, self.my * 0.5)
self.map_name_surface:set_opacity(0)
self.display_boss_name = false
else
self.display_boss_name = true
local i = 0
local text_group = sol.language.get_string("map.gameplay.boss_name."..map_name)
for text_lines in string.gmatch(text_group, "[^$]+") do
i = i + 1
if i == 1 then
self.map_name:set_text(text_lines)
self.mx, self.my = self.map_name:get_size()
self.map_name_surface = sol.surface.create(self.mx * 2 , self.my * 4)
self.map_name:draw(self.map_name_surface, self.mx, self.my * 0.5)
self.map_name_surface:set_opacity(0)
else
self.boss_presentation_text:set_text(text_lines)
local tw, th = self.boss_presentation_text:get_size()
self.boss_presentation_text:draw(self.map_name_surface, self.mx - 7 , (self.my * 0.5) + (i*7))
if tw > self.mx then self.mx = tw end
if th > self.my then self.my = th end
end
end
end
sol.timer.start(self, 500, function()
self.map_name_surface:fade_in(40)
sol.timer.start(self, 3500, function()
self.map_name_surface:fade_out(40, function()
self.map_name_surface:clear()
self:get_game():set_value("previous_map_name_displayed", nil)
end)
end)
end)
end
self:get_game():set_value("previous_map_name_displayed", map_name)
end
function map_metatable:get_displaying_map_name()
return self:get_game():get_value("previous_map_name_displayed")
end
function map_metatble:on_draw(dst_surface)
if self:get_displaying_map_name() ~= nil then
local map_name_width, map_name_height = self.map_name:get_size()
if self.display_boss_name then
self.map_name_surface:draw(dst_surface, (scr_x / 2) - (map_name_width) + 3, (scr_y / 2) + (map_name_height * 4.8))
else
self.map_name_surface:draw(dst_surface, (scr_x / 2) - (map_name_width), (scr_y / 2) - (map_name_height * 3))
end
end
end
function map_metatable:on_finished()
self:get_game():set_value("previous_map_name_displayed", nil)
end
In game :
Regular Map name
(http://i.imgur.com/tPI1nfU.png)
Boss name
(http://i.imgur.com/cpFQfSr.png)
These will definitely come in handy especially that fog script. Just thought I'd let you know that your work has not gone unnoticed!
The fog script is just a temporary solution before shaders and that's a simple script (a surface and a movement)
Though I noticed that the repeating thing is buggy, but that's fixable.
The next script would be the time system, but I am facing some issues with it, here's a sneek peak.
(http://i.imgur.com/sByRM8e.png)
It works, the clock movement work as well (https://www.youtube.com/watch?v=jtfXwwu7MaE) but the tone surface overlap with the "warp transition" and need custom timers to do nice transition between tone
Anyway, thanks :)