Author Topic: fog: Add fog to your map !  (Read 3343 times)

MetalZelda

  • Hero Member
  • *****
  • Posts: 551
    • View Profile
fog: Add fog to your map !
« on: February 07, 2017, 11:41:33 pm »
**Better Map Fog
Creation Date: Feb. 7 2016
Credits needed: You're free to decide by yourself.
Version: 1.3
Script needed:Multiple events by Christopho -> http://forum.solarus-games.org/index.php/topic,784.0.html

Version Changelog

Code: [Select]
--1.0--
Initial code
Can draw and move a surface, as well as setting the opacity
Method: Using map.dat

Code: [Select]
--1.1--
Bugfix: Strange behaviour on movement (movement not stopped, cutted surface), use a² = b² + c² and then root square it for surface's diagonal movement
Added: Opacity modulation
Method: Same as 1.0


Code: [Select]
--1.2--
Code has been revamped, data is loaded through a Lua file
Added: Surface coloration
Added: Surface blend mode modifer
Method: Parsing from maps/lib/fog_config.lua

Code: [Select]
--1.3--
Bugfix: Error when going on a map where there is no fog data

You might remember my very old map script that wander around the Development section, I've decided to update it, simpler, cleaner yet it is more efficient.
My idea for this update is straightly taken from RPG Maker, you know, there is a GUI that allows you to select your fog, movement and opacity, and The Legend of Zelda: Minish Cap (Fire Cave Dungeon) this simply is the same idea, but instead, it is in pure Lua.

**Documentation and technical presentation
In older version, fogs were drawn using a menu, and constantly cleaned and redrawn on each map changes.
Plus, you had to call a function to declare this fog, this was very annoying.
This method is way simpler, as it use the default map functions, no need to call a function to draw a fog anymore.

** Script calls
In order to make this script to work, you need:

1. A map metatable script
This is my map_metatable.lua header
Code: (lua) [Select]
  --
  local map_metatable = sol.main.get_metatable("map")
  require("scripts/fog")(map_metatable)
  --
  A child's play !

2. This script (See bellow) that you place in the /script folder (create it if it doesn't exist)
3. The "fog_config.lua" sccript in /maps/lib/ 

** Known bug
All fixed.

** How to use:
  //The best is to have a map metatable
 
  This is my map_metatable.lua header to load this script.
  --
  local map_metatable = sol.main.get_metatable("map")
  require("scripts/fog")(map_metatable)
  --
 
  See ? It's child's play!

** I want to draw a fog, teach me, master

This is the base that you need to learn in order to draw a fog / move it / play with it's opacity

Code: (lua) [Select]
["map_id"]= {
  fog                     = "fog",
  fog_speed               = speed,
  fog_angle               = angle,
  fog_opacity             = opacity,
  fog_depth               = use_depth,
  fog_detph_multiplier    = depth_multiplier,
  fog_opacity_range       = opacity_range,
  fog_opacity_wait_time   = opacity_wait_time,
  fog_opacity_update_time = opacity_update_time,
  fog_color = {r, g, b, a},
  fog_blend_mode = "blend_mode",
},

Description

"map_id"        (String)     = The id of your map, shorter, it's file name
"fog"           (String)     = The fog bitmap located in /sprites/fogs{
speed           (Integer)    = The speed of your fog (0 = static, 999 = very fast, don't go above 999 or else it will crash)
angle           (Integer)    = Number between 0 and 7 where 0 = east and 7 = south-east (counter clockwise)
opacity         (Integer)    = The opacity of your fog, must be a valid number between 0 and 255

--Optionnal
use_depth (Boolean) = Use a depth effect (optionnal), value must be booleans (true or false) or nil
depth_multiplier (Integer) = By default the depth multiply the movement by 1.5 to simulate faster fog movement when you move around, you can change this value (optionnal), value can be decimals and negative.
opacity_range (Table) = Range that determine the maximum and minimum opacity, must be a table value, with 2 values (minimum and maximum opacity)
opacity_wait_time (Integer) = Waiting time before the transition between max / min opacity and vice versa
opacity_update_time (Integer) = Waiting time between opacity +/-1
color  (Table) = Color (red, green, blue and alpha (transparency), values must be between 0 and 255)
blend_mode (String) = Set the fog's blend mode, see Solarus' method of applying blend modes for surfaces (http://www.solarus-games.org/doc/latest/lua_api_drawable.html#lua_api_drawable_set_blend_mode)
   
Example, I wanna display a fog in the map with the id "Dungeon7"
Code: (lua) [Select]
["Dungeon7"]= {
  fog = "forest_fog",
  fog_speed = 10,
  fog_angle = 3,
  fog_opacity = 75,
},
   
Another example: I want to draw a static image on the map with the id "forest"
Code: (lua) [Select]
["forest"]= {
  fog = "forest_cloud",
  fog_speed = 0,
  fog_angle = 0,
  fog_opacity = 75,
},

You can even use the Depth multiplier, if you don't set any value to it through fog_depth_multiplier, it's default value will be 1.5, yet you can specify your own value, even negative
Code: (lua) [Select]
["forest"]= {
  fog = "forest_cloud",
  fog_speed = 0,
  fog_angle = 0,
  fog_opacity = 75,
  fog_depth = true
},
   
Now, let's assume we're on a Fire Temple, we want a foggy thing with changing opacity, you can even use the Fog depth.
We can change the Opacity
   
Code: (lua) [Select]
["FireTemple_Room8"]= {
  fog = "fire_fog",
  fog_speed = 0,
  fog_angle = 0,
  fog_opacity = 75,
  fog_opacity_range = {50, 110},
  fog_opacity_wait_time = 1000,
  fog_opacity_update_time = 10,
},
The opacity will change each 10ms (1 frame)
When the max or minimum opacity has been reached, defined in fog_opacity_range, the script will pause for 1000 ms (1 sec) before resumine
   
Remember, this happen only in maps/lib/fog_config.lua
So, if you want to declare a fog
   
Code: (lua) [Select]
local fogs = {
  ["FireTemple_Room8"]= {
    fog = "fire_fog",
    fog_speed = 0,
    fog_angle = 0,
    fog_opacity = 75,
    fog_opacity_range = {50, 110},
    fog_opacity_wait_time = 1000,
    fog_opacity_update_time = 10,
  },

  ["forest"]= {
    fog = "forest_cloud",
    fog_speed = 0,
    fog_angle = 0,
    fog_opacity = 75,
  },
}

return fogs


**The Script
Code: (lua) [Select]
return function(map)
--[[
** Better map fog script
** Creation Date: Feb. 7 2016
** Credits needed: You're free to decibe by yourself.

** Script depencies:
  Multi events by Christopho (http://forum.solarus-games.org/index.php/topic,784.0.html)
 
** What you need:
  Create a folder in /sprites called "fogs" and place your fogs bitmap here.
  Create a file called fog_config.lua in the "maps/lib" folder, and in this file, copy/paste this
 
 
  local fogs = {
 
  }
  return fogs
 
  This is the base of this script,

** How to use:
  //The best is to have a map metatable
 
  This is my map_metatable.lua header
  --
  local map_metatable = sol.main.get_metatable("map")
  require("scripts/fog_manager")(map_metatable)
  --
 
  See ? It's child's play!
 
** Tutorial: Displaying a fog
 
  This is the whole things you need to know about this script.

["map_id"]= {
  fog                     = "fog",
  fog_speed               = speed,
  fog_angle               = angle,
  fog_opacity             = opacity,
  fog_depth               = use_depth,
  fog_detph_multiplier    = depth_multiplier,
  fog_opacity_range       = {opacity_range},
  fog_opacity_wait_time   = opacity_wait_time,
  fog_opacity_update_time = opacity_update_time,
  fog_color               = {r, g, b, a}
},

Description

"map_id"            (String)     = The id of your map, shorter, it's file name
"fog"               (String)     = The fog bitmap located in /sprites/fogs{
    speed               (Integer)    = The speed of your fog (0 = static, 999 = very fast, don't go above 999 or else it will crash)
            angle               (Integer)    = Number between 0 and 7 where 0 = east and 7 = south-east (counter clockwise)
            opacity             (Integer)    = The opacity of your fog, must be a valid number between 0 and 255
(Optionnal) use_depth           (Boolean)    = Use a depth effect (optionnal), value must be booleans (true or false) or nil
(Optionnal) depth_multiplier    (Integer)    = By default the depth multiply the movement by 1.5 to simulate faster fog movement when you move around, you can change this value (optionnal), value can be decimals and negative.
(Optionnal) opacity_range       (Table)      = Range that determine the maximum and minimum opacity, must be a table value, with 2 values (minimum and maximum opacity)
(Optionnal) opacity_wait_time   (Integer)    = Waiting time before the transition between max / min opacity and vice versa
(Optionnal) opacity_update_time (Integer)    = Waiting time between opacity +/-1
(Optionnal) color               (Table)      = Color (red, green, blue and alpha (transparency), values must be between 0 and 255)
    (Optionnal) blend_mode          (String)     = Set the fog's blend mode, see Solarus' method of applying blend modes for surfaces (http://www.solarus-games.org/doc/latest/lua_api_drawable.html#lua_api_drawable_set_blend_mode)


Example, I wanna display a fog in the map with the id "Dungeon7"
["Dungeon7"]= {
  fog                     = "forest_fog",
  fog_speed               = 10,
  fog_angle               = 3,
  fog_opacity             = 75,
},

Another example: I want to draw a static image on the map with the id "forest"
["forest"]= {
  fog                     = "forest_cloud",
  fog_speed               = 0,
  fog_angle               = 0,
  fog_opacity             = 75,
},

You can even use the Depth multiplier, if you don't set any value to it through fog_detph_multiplier, it's default value will be 1.5, yet you can specify
your own value, even negative
["forest"]= {
  fog                     = "forest_cloud",
  fog_speed               = 0,
  fog_angle               = 0,
  fog_opacity             = 75,
  fog_depth               = true
},

Now, let's assume we're on a Fire Temple, we want a foggy thing with changing opacity, you can even use the Fog depth.
We can change the Opacity

["FireTemple_Room8"]= {
  fog                     = "fire_fog",
  fog_speed               = 0,
  fog_angle               = 0,
  fog_opacity             = 75,
  fog_opacity_range       = {50, 110}
  fog_opacity_wait_time   = 1000
  fog_opacity_update_time = 10
},

Remember, this happen only in maps/lib/fog_config.lua

So, if you want to declare a fog

local fogs = {
  ["FireTemple_Room8"]= {
    fog                     = "fire_fog",
    fog_speed               = 0,
    fog_angle               = 0,
    fog_opacity             = 75,
    fog_opacity_range       = {50, 110}
    fog_opacity_wait_time   = 1000
    fog_opacity_update_time = 10
  },


}

return fogs
]]

  local movement
  local opacity_state = 0
  local fog_data = require("maps/lib/fog_config")
 
  local function load_map(map_id)
    local fog = {}
     
    -- This map was not found while parsing the fog config
    if fog_data[map_id] == nil then
      return
    end

    local map = fog_data[map_id]
if map ~= nil then
  fog[1] = {
fog                   = map.fog,
        fog_speed             = map.fog_speed,
fog_angle             = map.fog_angle,
fog_opacity           = map.fog_opacity,
fog_depth             = map.fog_depth,
fog_depth_multiplier  = map.fog_detph_multiplier,
fog_opacity_range     = map.fog_opacity_range,
fog_opacity_wait_time = map.fog_opacity_wait_time,
fog_opacity_update    = map.fog_opacity_update_time,
fog_color             = map.fog_color,
fog_blend_mode        = map.fog_blend_mode
  }
end

    return fog
  end
 
  local function update_opacity_surface(map)
    -- No opacity range, no need to continue
    if map.fog_opacity_r == nil then
  return
end

    local minimum, maximum = map.fog_opacity_r[1], map.fog_opacity_r[2]
sol.timer.start(map, map.fog_opacity_u, function()
  local opacity = map.fog:get_opacity()
  local new_opacity = opacity_state == 0 and 1 or -1
 
  if opacity == (opacity_state == 0 and maximum or minimum) then
sol.timer.start(map, map.fog_opacity_w, function()
  opacity_state = opacity_state == 0 and 1 or 0
  update_opacity_surface(map)
end)
    return
  end

  map.fog:set_opacity(opacity + new_opacity)

  return opacity ~= (opacity_state == 0 and maximum or minimum)
end)
  end
 
  -- Update the movement
  local function compute_movement(map)
    map.fog:set_opacity(map.fog_opacity)
local fog_size_x, fog_size_y = map.fog:get_size()

-- No speed, no need to continue
if map.fog_speed == 0 then
  return
  end

    local fog_angle = map.fog_angle
-- Get the Max movement in order to determine the max distance
-- Diagonal is determined by Pythagorean theorem a² = b² + c²
local diagonal = math.sqrt((fog_size_x * fog_size_x) + (fog_size_y * fog_size_y))
local angle = {
  fog_size_x,
  fog_size_y,
  fog_size_x,
  fog_size_y,
}

local max_distance =  fog_angle > 0 and angle[fog_angle / 2] or 0
if fog_angle % 2 ~= 0 then
  max_distance = diagonal
end

function restart_overlay_movement()
  movement = sol.movement.create("straight")
  movement:set_speed(map.fog_speed)
  movement:set_max_distance(max_distance)
  movement:set_angle(fog_angle * math.pi / 4)
  movement:start(map.fog, function()
map.fog:set_xy(0, 0)
restart_overlay_movement()
  end)
    end
restart_overlay_movement()
  end

  map:register_event("on_started", function(map)
    local data = load_map(map:get_id())

if movement ~= nil then
  movement:stop()
end

        if data == nil then
          return
        end

    for _, fog in ipairs(data) do
  map.fog           = sol.surface.create("fogs/".. fog.fog ..".png")
  map.fog_speed     = fog.fog_speed
  map.fog_opacity   = fog.fog_opacity
  map.fog_angle     = fog.fog_angle
  map.fog_depth     = fog.fog_depth
  map.fog_depth_mvt = fog.fog_depth_multiplier ~= nil and fog.fog_depth_multiplier or 1.5
  map.fog_opacity_r = fog.fog_opacity_range
  map.fog_opacity_w = fog.fog_opacity_wait_time
  map.fog_opacity_u = fog.fog_opacity_update
 
  if fog.fog_blend_mode ~= nil then
    map.fog:set_blend_mode(fog.fog_blend_mode)
  end
 
  if fog.fog_color ~= nil then
    map.fog:fill_color(fog.fog_color)
  end
 
  break
end



compute_movement(map)
update_opacity_surface(map)
  end)

  map:register_event("on_draw", function(map, dst_surface)
    local scr_x, scr_y = dst_surface:get_size()

    if map.fog ~= nil then
  local x, y = map:get_camera():get_bounding_box()
  local overlay_width, overlay_height = map.fog:get_size()
 
  if map.fog_depth ~= nil then
    x, y = -math.floor(x * map.fog_depth_mvt), -math.floor(y * map.fog_depth_mvt)
  else
    x, y = -math.floor(x), -math.floor(y)
  end

  x = x % overlay_width  - 2 * overlay_width
  y = y % overlay_height - 2 * 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
      map.fog:draw(dst_surface, dst_x, dst_y)
  dst_x = dst_x + overlay_width
    end
    dst_y = dst_y + overlay_height
  end
    end
  end)
end

Small example, Cloud.

Code: [Select]
  fog = "overworld_smallcloud",
  fog_speed = 10,
  fog_angle = 3,
  fog_opacity = 75,

This is how it will looks (minus the tone)



NO ! NO !



Another example: a static image

Parameters

Code: [Select]
  fog = "forest",
  fog_angle = 0,
  fog_speed = 0,
  fog_opacity = 150,



 
Code: (lua) [Select]
["normal/Dungeon/GoronSanctuary/boss/boss"] = {
    fog                     = "fire_mist",
fog_speed               = 10,
fog_angle               = 7,
fog_opacity             = 70,
fog_depth               = true,
fog_detph_multiplier    = nil,
fog_opacity_range       = {40, 110},
fog_opacity_wait_time   = 1000,
fog_opacity_update_time = 10
  },

Give this !
https://www.youtube.com/watch?v=36m3gcFFWao

Code: (lua) [Select]
  ["normal/Dungeon/GoronSanctuary/boss/boss"] = {
    fog                     = "fire_mist",
fog_speed               = 10,
fog_angle               = 7,
fog_opacity             = 70,
fog_depth               = true,
fog_detph_multiplier    = nil,
fog_opacity_range       = {40, 110},
fog_opacity_wait_time   = 1000,
fog_opacity_update_time = 10,
fog_color               = {255, 0, 0, 150},
  },

Give this :
https://www.youtube.com/watch?v=yia2Xy_U3_g
« Last Edit: February 09, 2017, 10:31:50 pm by MetalZelda »

Christopho

  • Administrator
  • Hero Member
  • *****
  • Posts: 1172
    • View Profile
Re: fog: Add fog to your map !
« Reply #1 on: February 08, 2017, 08:56:50 am »
- Lua is not an acronym and should not be written in capital letters.
- You can't change the format of .dat files. If the user open the map with the quest editor, changes it and saves it again, your additional stuff will disappear. Just put the fog information in an external script file instead.
- require("scripts/fog")(map_metatable) is weird. Just do require("scripts/fog"). And it is the job of that script to get the map metatable if they want. When you need to pass a paramater to some external script, do it with a proper function:
Code: (lua) [Select]
local fog_manager = require("scripts/fog")
fog_manager:my_function(some_parameter)

MetalZelda

  • Hero Member
  • *****
  • Posts: 551
    • View Profile
Re: fog: Add fog to your map !
« Reply #2 on: February 08, 2017, 11:07:21 am »
- Lua is not an acronym and should not be written in capital letters.
- You can't change the format of .dat files. If the user open the map with the quest editor, changes it and saves it again, your additional stuff will disappear. Just put the fog information in an external script file instead.
- require("scripts/fog")(map_metatable) is weird. Just do require("scripts/fog"). And it is the job of that script to get the map metatable if they want. When you need to pass a paramater to some external script, do it with a proper function:
Code: (lua) [Select]
local fog_manager = require("scripts/fog")
fog_manager:my_function(some_parameter)

Well then, it's gonna be fixed in the next update  :)
I can already say that the way to make a fog already changed inthe update, all fogs are loaded from a single file

Code: (lua) [Select]
fogs{
  --[[
    Data for the fog to display on a specific maps
    The format for declaring and displaying a fog for a map is the following

    ["map_id"]= {"fog", speed, angle, opacity, use_depth, depth_multiplier},

    You can edit these value while playing, the script is reloaded when a map start.
  ]]
 
  ["unplayable/titlescreen"] = {"forest", 0, 0, 75, true, 1.5},
  ["Dungeon9"] = {"fire_mist", 10, 3, 50},
}
« Last Edit: February 08, 2017, 02:22:47 pm by MetalZelda »

Diarandor

  • Hero Member
  • *****
  • Posts: 1045
  • Cats are cool! (ΦωΦ)
    • View Profile
Re: fog: Add fog to your map !
« Reply #3 on: February 08, 2017, 04:48:40 pm »
Small example, Cloud.

Code: [Select]
  fog = "overworld_smallcloud",
  fog_speed = 10,
  fog_angle = 3,
  fog_opacity = 75,

This is how it will looks (minus the tone)



NO ! NO !

LOL!!! ;D
This confirms that all great devs are crazy, hehehe! :D
“If you make people think they're thinking, they'll love you. But if you really make them think, they'll hate you.”

Christopho

  • Administrator
  • Hero Member
  • *****
  • Posts: 1172
    • View Profile
Re: fog: Add fog to your map !
« Reply #4 on: February 08, 2017, 05:28:28 pm »
Why don't you keep the same format as in your previous message? The syntax was great, what was wrong was just do to it in the map .dat file. String keys are more readable:
Code: (lua) [Select]
local fogs = {
  ["unplayable/titlescreen"] = {
    fog = "forest",
    angle = 0,
    speed = 0,
    opacity = 75,
    use_depth = true,
    depth_multiplier = 1.5
  },
}
return fog

Other examples:
https://github.com/solarus-games/zelda-roth-se/blob/dev/data/scripts/dungeons.lua
https://github.com/solarus-games/zelda-mercuris-chest/blob/dev/data/scripts/hud/hud_config.lua
https://github.com/solarus-games/zelda-roth-se/blob/dev/data/items/hookshot_config.lua

MetalZelda

  • Hero Member
  • *****
  • Posts: 551
    • View Profile
Re: fog: Add fog to your map !
« Reply #5 on: February 08, 2017, 06:47:59 pm »
Post updated

Version 1.1

Added:
- Depth (optionnal)
- Opacity variation
- Bug fixed

EDIT

Version 1.2

Added:
- Color modifer
- Blend mode modifer

Everything you need to know is in the first post
« Last Edit: February 08, 2017, 08:54:16 pm by MetalZelda »

ffomega

  • Full Member
  • ***
  • Posts: 223
    • View Profile
    • Solarus Resource & Tutorial Site
Re: fog: Add fog to your map !
« Reply #6 on: July 01, 2017, 02:22:56 pm »
I created a much simpler way to add fog to your maps, using a singular overlay that is built right into my tileset, that uses parallax scrolling.  Simply take it, and (if you like, any one of the semi-transparent tiles located at the top of the tileset, fit it over the entire map, making sure it is on the highest layer, separate from all other tile patterns.  You may choose to make it dynamic (in case you want to have a script attached to it, for instance, creating a fog overlay for before the hero obtains the master swordd, and then having it destroyed or not created when he has it).
My tilesets page can be found here:
http://absolute-hyrule-tutorials.solarus-games.org/

MetalZelda

  • Hero Member
  • *****
  • Posts: 551
    • View Profile
Re: fog: Add fog to your map !
« Reply #7 on: July 02, 2017, 05:11:21 pm »
I created a much simpler way to add fog to your maps, using a singular overlay that is built right into my tileset, that uses parallax scrolling.  Simply take it, and (if you like, any one of the semi-transparent tiles located at the top of the tileset, fit it over the entire map, making sure it is on the highest layer, separate from all other tile patterns.  You may choose to make it dynamic (in case you want to have a script attached to it, for instance, creating a fog overlay for before the hero obtains the master swordd, and then having it destroyed or not created when he has it).

That's another way to use it, this script is for permanent overlays from map creation to map destruction (I might update it one day)
This is also for minish cap styled fog, like sunrays or fire haze (ie. with opacity variation)
But I think both of our methods can be replaced by shaders when this will be possible  :P