Menu

Show posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.

Show posts Menu

Topics - MetalZelda

#1
General discussion / .midi / .spc to .it format ?
March 30, 2018, 11:59:56 PM
Hello.

I have a bunch of mid files that I have in my computer, with various changes done to the soundfont (If you don't understand then check this video https://www.youtube.com/watch?v=-UwhEyxFrns) and I plan to use the sol.audio.set_music_channel_volume(channel, volume) for various things (time of day based music, environment stuffs)

However, there's few, if no, no real tutorial on how this can be achieved. There is a spc to it that works but there's some problems on some sustained note but works great, there's none for mid but i'm pretty sure it's feasible.

Any idea ?
#2
Your scripts / Roof Fading script
March 26, 2018, 05:45:06 PM
This was a script I had in mind some times ago and decided to put this to my project, this was done in 2 hours of hard debugging and getting right values in February 2018, it was revised yersteday and now use userdata rather than dynamic tiles.
This is hardly based on Project Zelda Engine's system, this time, you don't have to cut and make graphics for this, you just need to do some boring stuffs using table, programmers love tables, everyone love tables

A little resumé of what it does in glorious glitchy hypercam 4 which does not render any justice to the video
https://www.youtube.com/watch?v=9H0unEaBCwY

As you might see, this is a cool "room transition", if you have a hugeload of rooms in only 1 maps this is a cool workaround, this works excellently on inside type tilesets. I personnaly use it on Houses and Dungeons

For this script, you need :
- [Multi Events] by Christopho (http://forum.solarus-games.org/index.php/topic,784.0.html)
- Create a folder called "roof_texture" in /sprites and put all your desired roof texture in there


To use this script, you need :
- Open main.lua
- C/P this code in main.lua, remember to change path_of_the_script to the path of this script
Code (lua) Select

local map_metatable = sol.main.get_metatable("map")
require(path_of_the_script)(map_metatable)


Let's talk real deal will ya

Code (lua) Select
--[[
  Fadable rooms
  Made by MetalZelda
  Inspired by: Project Zelda Engine
  Feb.2018 - Rev Mar.2018
  Version 1.6
 
  REQUIRED:
 
  - [Multi Events] by Christopho
    (http://forum.solarus-games.org/index.php/topic,784.0.html)

  Simulate multiple rooms by hiding one using a table

  {
    texture = {
src = "texture_path",
opacity = 0 - 255,
}
    x = x position of the roof
    y = y position of the roof
    width = horizontal lenght of the roof
    height = vertical lenght of the roof
    color = {r, g, b} (color of the roof)
  }
 
  color can be omnitted if using a texture
  texture can be omnitted if using a color preset
 
  You can combine both by using a semi-transparent texture and apply a color, the color will be drawn bellow

  Trigger zone is defined by the script bellow
  Usually: rooms.under = (x >= rooms.x - 9 and x <= rooms.x + rooms.width + 9) and (y >= rooms.y - 6 and y <= rooms.y + rooms.height + 23)
]]

return function(map)
  -- This is the incrementation value, 8 is faster enough
  local opacity_increment = 8

  map:register_event("on_started", function(map)
    -- If nothing exists on the rooms table means that there are nothing to display, the scripts stops here.
    if map.fade_rooms == nil then
  return
end

-- Store additionnal data on the roof table such as surface, color and opacity (default = 255) that can be automatically set by the script
for num, rooms in ipairs(map.fade_rooms) do
  rooms.surface = sol.surface.create(rooms.width, rooms.height)
 
  if rooms.color then
    rooms.surface:fill_color(rooms.color)
  end
 
  if rooms.texture then
    local texture_surface = sol.surface.create("roof_texture/" .. rooms.texture.src .. ".png")
    local texture_width, texture_height = texture_surface:get_size()

if rooms.texture.opacity then
      texture_surface:set_opacity(rooms.texture.opacity)
end

-- Get the total pixels to cover
local total = ((rooms.width * rooms.height) /  (texture_width * texture_height))

local x = 0
local y = 0
 
for i = 1, total do
  -- Draw the texture on the right place
  texture_surface:draw(rooms.surface, x, y)
 
  -- Increment next value
  x = x + texture_width
 
  if x >= rooms.width then
    x = 0
    y = y + texture_height
  end
end    
  end
 
  rooms.opacity = 255
end
  end)
 

  map:register_event("on_draw", function(map, dst_surface)
    -- To avoid errors, if there is nothing to draw, abort.
    if map.fade_rooms == nil then
  return
end

    for _, rooms in ipairs(map.fade_rooms) do
  local cam_x, cam_y = map:get_camera():get_position()
  rooms.surface:draw(dst_surface, rooms.x - cam_x, rooms.y - cam_y)
 
  local opacity = rooms.opacity
 
  -- Get player position, current layer is not a necessary parameter.
  local x, y = map:get_hero():get_position()
 
  -- Define the rule of being under a roof for any roof.
  rooms.under = (x >= rooms.x - 9 and x <= rooms.x + rooms.width + 9) and (y >= rooms.y - 6 and y <= rooms.y + rooms.height + 23)
 
  -- Define opacity variation for roof
  if rooms.under or rooms.force_under then
    rooms.opacity = rooms.opacity > (opacity_increment - 1) and rooms.opacity - opacity_increment or 0
  else
    rooms.opacity = rooms.opacity < (255 - opacity_increment) and rooms.opacity + opacity_increment or 255
  end
 
  -- Don't need to update any opacity if the game is paused.
  if map:get_game():is_paused() then
    rooms.opacity = opacity
  end
   
  rooms.surface:set_opacity(rooms.opacity)
end
  end)
 
  function map:dim_room_surface(i, bool)
    if bool == nil then
  bool = self.fade_rooms[i].force_under == nil and true or (not self.fade_rooms[i].force_under)
end
    self.fade_rooms[i].force_under = bool
  end
end


You might have a hero that have other size than Link, you can change the trigger range in here
Code (lua) Select
rooms.under = (x >= rooms.x - 9 and x <= rooms.x + rooms.width + 9) and (y >= rooms.y - 6 and y <= rooms.y + rooms.height + 23)

I did everything I've done but nothing work, give monney back

This is what you should do in your map script in order to make this to work
Let's have the video example above

You can use this texture for testing
https://i.imgur.com/0BwDrP8.png

and rename it "forest_roof_txt0.png"

Code (lua) Select

local map = ...

local fade_roofs = {
  {
    texture = {
      src = "forest_roof_txt0",
      opacity = 120
    },
    x = 80,
  y = 256,
    width = 272,
    height = 200,
  color = {56, 40, 40}
  },

  {
    x = 80,
  y = 16,
    width = 272,
    height = 200,
  color = {56, 40, 40}
  },
}

map.fade_rooms = fade_rooms


As you can see, there are 6 needed parameters

Code (lua) Select

{
  texture = {src = src of the image, opacity = opacity (0 - 255}
  x = x position of the roof
  y = y position of the roof
  width = horizontal lenght of the roof
  height = vertical lenght of the roof
  color = {r, g, b} (color of the roof)
}


If you forget one of them, your computer might explode and then stuff in red will appear, Diarandor might send you cats, you've been warned.

Errors might not exists since everything is generated by the user.

If the script don't work:
- One of your map script use the map:on_started() function rather than map:register_event("on_started")
- One of your map script use the map:on_draw() function rather than map:register_event("on_draw")
- Bad script path

Credits:
Not needed, you are free to decide.
#3
Development / Randomnize table ?
December 10, 2017, 02:31:53 PM
Hello

Today I am mostly programming minigames, one of which is based on ALTTP.
But, I have taken another view of how the minigame would work.
Winnable items are shown before the game starts like so



Then a movement starts on winnable item's entity and the minigame starts.

This leads me to this question to make the minigame more dependant of an RNG (Random Number Generator) to make the minigame a bit more difficult
Winnable items are stored in a table

Code (lua) Select
local minigame_prices = {
  {"rupee", 0},
  {"amber_counter", 0},
  {"green_chuchu_counter", 0},
  {"heart_piece", 0},
  {"arrow", 0},
  {"rupee", 3},
  {"heart", 0},
  {"rupee", 5},
  {"rupee", 2},
}


Basically, it is

[chest 1] treasure = rupee, variant = 0

All I want is

[chest (random 1 - 9)] treasure = rupee, variant = 0

Any idea of how to make it happen ?
#4
Hi

I'm having troubble with cutscenes and sprites management.
I am currently making sage cutscenes with as inspiration ALTTP, but, after a while, the crystal around the sage needs to be removed



The sprite creation is a bit different because the cutscene starts immediately after the boss died, a surface fades to accomplish the black background, a menu handles the whole cutscene (mostly to overwrite and avoid pausing / moving, doing actions during cutscene)

Sprites are created in this order

black surface
Link sprite
Sage sprite
Crystal sprite

All created by sol.surface / sprite.create

But, calling

sprite:remove()

this happen

Error: In timer callback: [string "maps/cutscene_data/SageScene_0.lua"]:95: attempt to call method 'remove' (a nil value)

I can't use map entities because the menu is started above everything on the map, entities are not visible during the cutscene because of the background surface
Which confuse me a bit, we can't remove a sprite ?
#5
Development / [SOLVED] Trouble porting a script
June 25, 2017, 04:40:28 PM
Hello.

So, I am porting a RUBY script to lua.
This script will allow easier pause menu creation, replacing cursor position by user defined positions. Like so

Code (lua) Select

local elements = {
  config = {save = true},
  {"rupee_bag", 68, 78, "info"},
  {"bomb_bag", 68, 104, "info"},
  {"quiver", 68, 130, "info"},
  {"deku_nuts_bag", 68, 156, "info"},
  {"bombchu_bag", 68, 182, "info"},
  {"sword", 217, 84, "action"},
  {"tunic", 209, 180, "action", variant = "_1"},
  {"tunic", 233, 180, "action", variant = "_2"},
  {"tunic", 257, 180, "action", variant = "_3"},
  {"glove", 209, 132, "info"},
  {"shield", 233, 132, "info"},
  {"climb_ring", 257, 132, "info"},
  {"sword_biggoron", 249, 84, "action"},
}


The 2nd and 3rd index is used to place an item on the X and Y axis, as well as the cursor

This is the RUBY script I'm trying to port

Code (ruby) Select
# Initialize variables for the weightings on the vertical and horizontal
      # directions
wv, wh = 0, 0
      # The weightings for the secondary and primary axis relative to input
w1, w2 = 1, 2
      # Initialize a variable for the direction and get the direction
      # pressed by the user
      input_dir = 0
      input_dir = 2 if Input.repeat?(Input::DOWN)
      input_dir = 4 if Input.repeat?(Input::LEFT)
      input_dir = 6 if Input.repeat?(Input::RIGHT)
      input_dir = 8 if Input.repeat?(Input::UP)
      # Do nothing if no button was pressed
      return if input_dir == 0
      #get the item
      item = self.item
      #if the item is left toggle and the input direction is left
      if item == :left_toggle && input_dir == 4
        #move to the left window
        return $scene.move_window(6)
      #elsif the item is right toggle and the input direction is right
      elsif item == :right_toggle && input_dir == 6
        #move to the right window
        return $scene.move_window(4)
      end
      # Setup the weightings for each direction
if  (input_dir == 2 or input_dir == 8)
wv, wh = w1, w2
elsif (input_dir == 4 or input_dir == 6)
wv, wh = w2, w1
end
      # Get the xy position of the current item
cxy = @xy_pos[@index]
      # Create default value for the closest item's distance (absurdly large)
      closest_index = -1
closest_distance = 9999999
      # Loop through every item
for i in 0...@xy_pos.size
# Skip if it is the old item or is in the opposite direction
if @index == i or
   (input_dir == 2 and @xy_pos[i][1] < cxy[1]) or
   (input_dir == 4 and @xy_pos[i][0] > cxy[0]) or
   (input_dir == 6 and @xy_pos[i][0] < cxy[0]) or
   (input_dir == 8 and @xy_pos[i][1] > cxy[1])
next
end
        # Get the distance from the last highlighted item to this tested one
dist_h = (@xy_pos[i][0] - cxy[0]).abs
dist_v = (@xy_pos[i][1] - cxy[1]).abs
        # If it is really against the primary axis
        if (wh == w1 and w2 * dist_h < dist_v) or
            (wv == w1 and w2 * dist_v < dist_h)
          next
        end
        # Get the distance multiplied with their weightings
dist = dist_h * wh + dist_v * wv
        # Set at closest item if appropriate
if (dist < closest_distance)
closest_distance = dist
closest_index = i
end
end
      # Do nothing if nothing was selected
      return if closest_index == -1
      @index = closest_index
      #move cursor
      move_cursor


@xy_pos is already defined in the elements variable, @cxy is the current cursor position, they are already defined

The only problem, RUBY have a skip iterator whereas I can't find the closest thing for lua

This is my attempt in Lua, which still fails, the cursor will still highlight the current item forever

Code (lua) Select

function submenu:set_cursor_position(command)
  local width = sol.video.get_quest_size()
  width = width / 2 - 160

  local current_x = self.cursor_position.x
  local current_y = self.cursor_position.y

  -- Create default value for the closest item's distance
  local closest_index = -1
  local closest_distance = 320

  -- Initialize variables for the weightings on the vertical and horizontal direction
  local wv, wh = 0, 0

  -- The weightings for the secondary and primary axis relative to input
  local w1, w2 = 1, 2

  -- Setup the weightings for each direction
  if (command == "down" or command == "up") then
    wv, wh = w1, w2
  elseif (command == "left" or command == "right") then
wv, wh = w2, w1
  end
 
  for i = 1, #self.items do
    local item = self.items[i]
local item_x = item.x
local item_y = item.y
local current_item_index = self:find_item(self:find_item_axis(self.cursor_position.x, self.cursor_position.y))

--  Skip if it is the old item or is in the opposite direction
if current_item_index == i or (command == "down" and item_y < current_y) or (command == "left" and item_x > current_x) or (command == "right" and item_x < current_x) or (command == "up" and item_y > current_y) then
  -- tried goto
end

-- Get the distance of the current item and the new one
local dist_h = math.abs(item_x - current_x)
local dist_v = math.abs(item_y - current_y)

if (wh == w1 and w2 * dist_h < dist_v) or (wv == w1 and w2 * dist_v < dist_h) then
  goto matched
end

::matched::
    -- # Get the distance multiplied with their weightings
local dist = (dist_h * wh + dist_v * wv)
 
-- # Set at closest item if appropriate
if (dist < closest_distance) then
  closest_distance = dist
  closest_index = i
end
  end

  local item = self.items[closest_index]
  local x = item.x
  local y = item.y - 5

  self.cursor_position = {x = x, y = y + 5}
  self.cursor_sprite_x = x + width
  self.cursor_sprite_y = y
  self.game:set_custom_command_effect("action", item.effect)

  local caption_item = item.item .. "_" .. self.game:get_item(item.item):get_variant()
  if item.item == "tunic" then
    caption_item = "tunic_" .. self:get_tunic_variant()
  end
 
  self:set_caption(self.caption_text_keys[caption_item]) 
  self.game:set_value("equipment_last_index", position)
end



Is there a Lua equivalent of "next" ?

Next = https://www.tutorialspoint.com/ruby/ruby_loops.htm
Jumps to the next iteration of the most internal loop. Terminates execution of a block if called within a block (with yield or call returning nil).
#6
This is a straight port of a RUBY script. Made by MaximusMaxy, from Project Zelda Engine
This script allow to have an animated cursor for your pause menu, like Majora's Mask, of course, it can be adapted for other stuffs

This is an alternative solution for avoiding a spriteset creation (frame by frame cursor animation), this script calculate the animation for you.

You need:

-> a cursor png (1 part or 4 sides) which you should save in /sprites/menus/src/pause_submenus/cursor.png (You can change the file bellow
-> using 4 side cursor ? Just set use_rotation to false
-> Using 1 part cursor ? Set use_rotation to true

What can you do ?

-> You can modify the radius of the circle (minimum, maximum)
-> You can modify the speed of the animation (speed, where 1 = super slow and 9 = 1 lightyear per sanic)
-> Disable the rotation system and only keep the offset system
-> Disable the fading animation that occurs when the animation is drawn

Code (lua) Select
function submenu:create_cursor()
  -- Set the minimum and maximum radius of the rotation
  local minimum, maximum = 10, 12
 
  -- Speed of the animation (1 = slow, 9 = fast)
  local speed = 5
 
  -- Use the rotation system or just the zoom ? (true = use the rotation, false = just the zoom)
  local use_rotation = true
 
  -- Use the fading option
  local use_fade = true
 
  -- Cursor file
  local cursor = sol.surface.create("menus/src/pause_submenus/cursor.png")
  local cursor_width, cursor_height = cursor:get_size()
 
  -- Determine the region parts depending on use_rotation state
  if not use_rotation then
    cursor_width = cursor_width / 2
    cursor_height = cursor_height / 2
  end
 
  -- Don't touch this
  local count = 0
 
  function submenu:draw_cursor(dst_surface, x, y)
    -- Increment the rotation
    count = (count + speed) % 360
 
    -- Compute the sin and cos of the rotation
    local sin = math.sin(count * math.pi / 180)
    local cos = math.cos(count * math.pi / 180)
 
    -- Compute the zoom scaling
    local offset = minimum + (maximum - minimum) / 2 + (maximum - minimum) / 2 * sin

-- Update the cursor opacity
if use_fade then
  cursor:set_opacity(205 - (0.5 + sin) * 100)
end

-- Not using the rotation system, no rotation
if not use_rotation then
  cos, sin = 1, 1
end

local rotation_w = use_rotation and 0 or cursor_width
local rotation_h = use_rotation and 0 or cursor_height

-- Draw the cursors
-- Upper Right
    cursor:draw_region(rotation_w, 0, cursor_width, cursor_height, dst_surface, x + (sin * offset), y + (cos * -offset))

-- Lower Left
    cursor:draw_region(0, rotation_h, cursor_width, cursor_height, dst_surface, x + (sin * -offset), y + (cos * offset))

-- Lower Right
    cursor:draw_region(rotation_w, rotation_h, cursor_width, cursor_height, dst_surface, x + (cos * offset), y + (sin * offset))

-- Upper Left
    cursor:draw_region(0, 0, cursor_width, cursor_height, dst_surface, x + (cos * -offset), y + (sin * -offset))
  end
end
submenu:create_cursor()


This is plug and play, at least if you use the MoS submenu system, you just have to call submenu:draw_cursor(dst_surface, x, y) in yout submenu script

You can also delete the rotation system for a zooming cursor, all you need to zoom is the local "offset"
#7
Hello

I ran out with a problem

I am working on cutscenes, that imply the hero to be frozen AND execute basic actions when the cutscene is active
the problem is, you can't simulate a command when the hero is frozen, I've tried to unfreeze him before the action occurs but when loading a spin attack from simulated command and if the player mispress the attack button, the animation is cancelled and the hero can move on it's own depending on the rules given (hero:unfreeze before the action or handling the command)

If the attack command is not pressed then the animation will play fine

I've tried to start a dummy menu like so, I also tried using sol.main:on_key_pressed, same

In this event, your input should do nothing except if a dialog is active ...
Code (lua) Select
function dummy:on_key_pressed(key)
  return not game:is_dialog_active()
end


But no matter what, even if I delete hero:freeze(), this prevent the hero from moving, but we can still cancel the simulated input, even if this rule (key_pressed) or on_command_pressed has been defined to prevent any input to interrupt the action

Anyone help ?

You can reproduce it simply

Code (lua) Select

game:simulate_command_pressed("attack")
sol.timer.start(1500, function()
  game:simulate_command_released("attack")
end)
#8
Hey

I've planned some things with my project which include Link skins.
By skins, i already planned

- Regular Link (Parallel Worlds skin)
- ALTTP Link
- AST Female Link
- AST Male Link
- Eldran (Male / female)

https://cloud.githubusercontent.com/assets/28355478/26052240/1c231a6a-3965-11e7-8723-116a956cda03.png

With this idea come the thing of giving stats to the player, such as

- Normal for regular Link (1 attack, 1 defence)
- Defensive for Female Link (0.5 attack, 2 defence)
- Attacks for Male Link (2 attack, 0.5 defence)
- Damage and Defence for ALTTP Link (2 attack, 2 defence)
- Still not decided for Eldran (maybe attack, defence, speed and swim ability)

Starting stats are 1 attack / 1 defence, of course, abilities (sword / shield) will modify these values

What do you all think of this ? Do you think that this could be something useful ?
Of course, the skins will need to be unlocked by doing quests or depending on the progression the player made
#9
hi

So while working on some stuffs, primarly cutscenes, something gets my attention.
Let's assume that the hero is at a fixed point (let's say X 100 Y 200)
The camera in situation 1 will be set at X 300 Y 200
The camera in situation 2 will be set at X 800 Y 150
These camera have as target, the hero

so, if we set a speed in the most basic way, let's say 500 and start the movement on the camera, the arrival time will not be the same. This is what leads me to this thread

Is there any calculation that will allow the camera in both situation 1 and 2 that will allow the camera to arrive to the hero at, let's say, 2 seconds, with speed calculated by this arrival time ?

I already know that the calculation will include the default speed (500) and the target arrival time (2000 ms)

Any help will be appreciated
#10
Greetings.

Recently i've been working on the final title screen based on RPG / Zelda BOTW.
And so comes this idea of unlimited saves number.



however, i wanna display all files stats here, i already know how to do it, the problem is, the menu is a scroll type menu, with as max_position, the number of saves, however, I can't figure out how to count how many files with "save" prefix exists, if anyone have a clue, i'll gladly take it
#11
Your scripts / fog: Add fog to your map !
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

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


--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



--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


--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.

  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

  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
#12
Hello

I was modifying a platform script (from Diarandor I guess), which was a simple platform script, and I decided to modify it so it allow custom movement, path movement, and in the future circular movement

Code (lua) Select
local entity = ...
local hero = entity:get_map():get_entity("hero")

-- Platform
-- changes:

-- size              -> Entity size
-- time_stopped      -> self.time_stopped
-- speed             -> self.speed
-- directionnal path -> self.path

--todo
--entity.movement_type (path / circular / straight / target)

-- And anything related to these movement

function entity:on_created()
  local size_x, size_y = self:get_size()
  self:set_size(size_x, size_y)
  self:set_origin(size_x / 2, size_y / 2)
  self:set_can_traverse("jumper", true)
  self:set_can_traverse_ground("hole", true)
  self:set_can_traverse_ground("deep_water", true)
  self:set_can_traverse_ground("lava", true)
  self:set_can_traverse_ground("traversable", true)
  self:set_can_traverse_ground("shallow_water", false)
  self:set_can_traverse_ground("wall", false)
  self:set_modified_ground("traversable")
  self:set_layer_independent_collisions(false)

  self:start_movement()
end

function entity:start_movement()
  local m = sol.movement.create("path")
  m:set_path(self.path)
  m:set_speed(self.speed)
  m:set_loop(true)
  m:start(self)
 
  self:add_collision_test("touching", function(_, other)
    if other:get_type() == "wall" or (other:get_type() == "custom_entity" and other:get_model() == "object/platform/limit") then
      self:on_obstacle_reached(m)
    end
  end)
end

function entity:on_obstacle_reached(movement)
  -- Reverse the movement, reverse the table
  for i = 1, math.floor(#self.path / 2) do
    local tmp = self.path[i]
    self.path[i] = self.path[#tbl - i + 1]
    self.path[#self.path - i + 1] = tmp
  end

  --Make the platform turn back.
  movement:stop()
  movement = sol.movement.create("path")   
  movement:set_path(self.path)
  movement:set_speed(self.speed)
  movement:set_loop(true)

  sol.timer.start(self, self.time_stopped, function()
    movement:start(self)
  end)
end

function entity:on_position_changed()
  -- Moves the hero if located over the platform.
  if not self:is_on_platform(hero) then return end
    local hx, hy, hl = hero:get_position()
    local direction4 = self:get_direction()
    local dx, dy = 0, 0 --Variables for the translation.
    if direction4 == 0 then dx = 1
    elseif direction4 == 1 then dy = -1
    elseif direction4 == 2 then dx = -1
    elseif direction4 == 3 then dy = 1
    end
    if not hero:test_obstacles(dx, dy, hl) then hero:set_position(hx + dx, hy + dy, hl) end
end

function entity:on_movement_changed(movement)
  --Change direction of the sprite when the movement changes.
  local direction4 = movement:get_direction4()
  self:set_direction(direction4)
end

function entity:is_on_platform(other_entity)
  --Returns true if other_entity is on the platform.
  local ox, oy, ol = other_entity:get_position()
  local ex, ey, el = self:get_position()
  if ol ~= el then return false end
  local sx, sy = self:get_size()
  if math.abs(ox - ex) < sx/2 -1 and math.abs(oy - ey) < sy/2 -1 then return true end
  return false
end


Everything works fine, the entity's movement, speed and delay is now customizable from the map script

However

This is the script call in the map script, the platform is respectively named "platform0" and tried to test a simple squarishpath

Code (lua) Select
local platform0 = map:get_entity("platform0")
platform0.path = {0, 0, 0, 0, 6, 6, 6, 6, 4, 4, 4, 4, 2, 2, 2, 2}
platform0.speed = 60
platform0.time_stopped = 1000


Running the game, no errors whatsoever, but as soon as the map is loaded, the game freeze, the only way to quit the game is by force closing it.

What's wrong ?
#13
Hello,

My question concerns on_command_pressed(command) and custom inputs.

The advantage with on_command_pressed is that it englobe both keyboard and joypad bindings with ease, so it is not necessary to have in each menu that need to check a custom input such functions (on_joypad_*_pressed/moved), the command customisation menu is an exception.

Is it possible to do a such thing ? for example, let's say that I implemented a new input "minimap" and I wanna add it as a build-in command for on_command_pressed(command)

Want an example, well

This is the command table of my game

Code (lua) Select

-- Group all key bindings, build in and custom. This is used in the Option submenu, File selection screen
-- This also get the savegame values of bindings.
game.command= {
--  Command           Keyboard           Joypad
  {"right"  , "_keyboard_right" , "_joypad_right"},
  {"up"     , "_keyboard_up"    , "_joypad_up_key"},
  {"left"   , "_keyboard_left"  , "_joypad_left_key"},
  {"down"   , "_keyboard_down"  , "_joypad_down_key"},

  {"item_1" , "_keyboard_item_1", "_joypad_item_1"},
  {"item_2" , "_keyboard_item_2", "_joypad_item_2"},
   
  {"attack" , "_keyboard_attack", "_joypad_attack"},
  {"action" , "_keyboard_action", "_joypad_action"},
  {"pause"  , "_keyboard_pause" , "_joypad_pause"},

  {"shield" , "keyboard_shield" , "joypad_shield"},
  {"minimap", "keyboard_minimap", "joypad_minimap"},
  {"camera" , "keyboard_camera" , "joypad_camera"},
  {"items"  , "keyboard_items"  , "joypad_items"}
}


Something like this for a command

Code (lua) Select

  function menu:on_command_pressed(command)
    if command == "minimap" then
      -- Run the code
    end
  end


Is more efficient than this

Code (lua) Select

  function menu:on_key_pressed(key)
    if key== game:get_value("keyboard_minimap") then
      -- Run the code
    end
  end

function menu:on_joypad_hat_moved(hat, state)
    C/C
  end

function menu:on_joypad_button_pressed(key)
    C/P
  end

function menu:on_joypad_axis_moved(axis, state)
   C/P
  end
#14
Development / Custom sword ?
January 11, 2017, 04:38:08 PM
Hello.

So, Diarandor, I see that you use a custom sword for your project that I personnaly like since I want to be as close as Wind Waker / Minish Cap styled gameplay, I am currently reworking the hero's abilities for my project, the shield is being totally custom as well (Mirror Shield need other stuffs).

But, shield is simple to make,it's just something that you constantly synchronise on the hero and cancel when the hero state change or if the input is released, a simple function can handle enemy collision related stuffs, on a sword, it is different, I need some advice on many things such as the sword tapping and the spin attack related thing, are you using a timer in on_command_pressed or something like that ?
#15
Bugs & Feature requests / surface:fade_in/out issue ?
January 05, 2017, 12:11:00 AM
Hello.

When fading a surface by using fade in or fade out, our goal is to have the surface's opacity to 255 or 0, but the opacity, is never at 0 or 255, is this intentionnal ?
#16
Development / Avoid pickables to be ... picked ?
December 28, 2016, 01:00:51 AM
Hi.

So today I was working on streams (fan & water stream), so it implied also reworking an item: the Roc's Cape.
The roc's cape is a simple, yet complex item, script-wise.

The Hero's sprite coordinate are always changing while using the roc's cape, but it's collision stays (see bellow) on ground, which means, teleporters and switches are disabled if the player use this item, as well as being invincible to avoid any collision with enemies.



All works fine, but picking an item while flying is weird, and there is no pickable:set_obtainable(boolean) or whatever to avoid the hero to pick any items

Any workaround or solution ?
#17
Hi.

When I was working on some scripts something went wrong.
I am using different surfaces to display a text, but this surface change it's alignement depending on the content itself.

However ...

The text surface is created in on_started, like so

Code (lua) Select

self.text = sol.text_surface.create( blablabla )


There is a problem if I'm trying to change the text alignment on another function, for instance, in on_command_pressed()

Code (lua) Select
self.text:set_horizontal_alignment("left")

An error occurs

calling set_horizontal_alignment on bad self (string expected, got userdata))

However, self.text:get_horizontal_alignment() works ...
#18
Bugs & Feature requests / Dungeon map scaling issue ?
December 10, 2016, 02:47:21 PM
Hi.

So, while making dungeon minimap for the pause menu, I ran into a problem.
The map background is a placeholder from BoM

If I set the scale of the dungeon floor to the size of the map (for testing if scaling is right), the placement of bitmaps and the position of things can be wrong.

Here, I placed Link at coordinate 0, 0 (8, 13) and the map location on this world is set to 0, 0



You might see that here, the scaling is correct, I purposely show the configuration file so you might be able to reproduce it all by yourself

But, if I set the width / height of the minimap scaling to the size of the room, this happen



Link's position is still 8, 13 and map coordinate in the world is still 0, 0.

I use the MoS and BoM menu, both are similar. So you might be able to reproduce this bug.

My guess is, the faulty part is on map_submenu:to_dungeon_minimap_coordinates(x, y)

#19
General discussion / At which framerate Solarus runs ?
November 09, 2016, 01:11:52 AM
This is an interesting question, we never got to face this question.

But, as I was reworking my time system script (yet again), I was wondering, should the time be incremented in on_update or in on_draw, pretty dull though you might say and these two functions update at almost the same rate (each screen refresh)

But, what is this ... refresh rate ...
Interestingly, I did a print(count) to see what amount we need before incrementing the minute counter, like so

Code (lua) Select

function menu:on_started()
  self.count = 0
end

function menu:on_draw(dst)
self.count = self.count + 1
end


self.count increases infinitely and would never end, so, I wanted to be sure about the formula 1 second in real life = 1 second in the game, depending on the time flow.
Setting the condition (if self.count >= 30) where 30 (might) be the framerate, something got wrong, the time flow rate is faster than expected
But, setting the limitation to 60 makes the time to go normal.

So, enlight me, I don't know much about framerate and stuffs, but if on_draw is called a each cycle (so each frame recheck) and if we need 60 refresh for the engine so 1 second irl = 1 minute in game,does this make Solarus to run at 60FPS ?
Also Dxtory, a screen recorder always display 60 as framerate when recording some stuffs
#20
General discussion / Rip the tiles from Graal Classic
October 16, 2016, 02:14:24 PM
Hello !

As you might know, iPhone users have a good Online Zelda-like game called Graal Classic
The ressource used is based on A Link To The Past as well, every thing (characters, tiles) are from ALTTP with personnal modifications, and I am here to discuss about ripping all of Graal Classic ressources, it include more options then the default ALTTP tiles.
We could use them to expand the ALTTP ressource pack

What do you think ?