[WIP] Ceiling dropping effect (ALTTP)

Started by PhoenixII54, October 02, 2017, 10:01:07 PM

Previous topic - Next topic
October 02, 2017, 10:01:07 PM Last Edit: October 23, 2017, 05:00:28 PM by PhoenixII54
Hi, in my attempts to reproduce famous effects from ALTTP to compensate the missing effects in Zelda : Mystery of Solarus, here is one of the major ones :

When you fall in e hole that has a floor below, you end up in the said room, falling from the ceiling and dropping onto the floor, after doing a few turns oaround yourself.

Be prepared, besause this is the puropse of this script.
Code ( lua) Select

--Ceiling dropping system (WIP)
--Reproduces the "fall from uper floors" effects

--Usage : Call this script from any game launching system or use the event manager script to set the metatables then do your maps as usuel

--Extracting actual destination and ground from teletransporters since map;on_started(destination) dosen't take special destinations in account, then stirong it to a savegame variable lCan be useful for dungeons like Skull Dungeon (Level 3 of ALTTP, which has many hole entrances).
--Note, here, everything is in a single script, but in a real project you may have multiple file (one for each metatable)

local teletransporter_meta=sol.main.get_metatable("teletransporter")
local map_meta=sol.main.get_metatable("map")

function teletransporter_meta:on_activated()
  local game=self:get_game()
  local ground=self:get_map():get_ground(game:get_hero():get_position())
  game:set_value("tp_ground",ground)
end

--the actual trigger
function map_meta:on_started(destination)
  local hero=self:get_hero()
  local game=self:get_game()
  local x,y=hero:get_position()
  local ground=game:get_value("tp_ground")
  if ground=="hole" then
    --Falling from the ceiling
    hero:set_visible(false)
    hero:freeze()
    --disabling teletransoprters to avoid
    local disabled_teletransoprters={}
    for t in self:get_entities_by_type("teletransporter") do
      if t:is_enabled() then
        disabled_teletransporters[#disabled_teletransporters+1]=v
        t:set_enabled(false)
      end
    end
    --Creating a "stunt actor" moving vertically from the ceiling
    local falling_hero=self:create_custom_entity({
      name="falling_link",
      x=x,
      y=math.max(y-100,24),
      direction=0,
      layer=self:get_max_layer(),
      sprite=hero:get_tunic_sprite_id(),
      width=24,
      height=24,
    })
    falling_hero:get_sprite():set_animation("stopped")
    falling_hero:set_can_traverse_ground("wall",true)
    falling_hero:set_can_traverse_ground("empty",true)
    falling_hero:set_can_traverse_ground("traversable",true)

    --Creating a reception platform (prevents the hero from falling into consecutive holes during the animation)
    local platform=self:create_custom_entity({
      name="platform",
      direction=0,
      layer=layer,
      x=x,
      y=y,
      width=32,
      height=32,
      sprite="entities/shadow",
    })
    platform:bring_to_front()
    platform:get_sprite():set_animation("big")
    platform:set_modified_ground("traversable")

    --Creating the actual movement for the stunt actor
    local movement=sol.movement.create("target")
    movement:set_target(x,y)
    movement:set_speed(96)  --The falling speed.
    --Spinning the stunt actor on itself
    sol.timer.start(falling_hero, 100, function()
      falling_hero:set_direction((falling_hero:get_direction()+1)%4)
      return true
    end)
    movement:start(falling_hero, function()
      --Movement is now complete, restoring the disabled teletransoprters and getting rid of the temporary entities
      sol.audio.play_sound("hero_lands")
      platform:remove()
      falling_hero:remove()
      hero:set_visible(true)
      hero:unfreeze()
      for _,t in pairs(disabled_teletransporters) do
        t:set_enabled(true)
      end
    end)
    --]]
  end
end


Hope it will be useful, but it may have have bugs, so don't hasitate to report them to me. And remember, it is not fully finished so expect changes to happpen.
Remember that you may want to modify this script to make it compatible with Christopho's Multi-event script, which i don't use (yet) in my experiments to keep total control over my tests.

Anyway, enjoy !

edit#1 : Actually finished the v1.0 version, with ground detection on teletransportation.
Update (2017/10/23) : Modified the script to remove the extra sprite requirement and to add more flexibility. This is now likely a final version unless someone finds a more efficient way.

December 17, 2017, 03:39:42 PM #1 Last Edit: December 17, 2017, 07:24:06 PM by MetalZelda
I like this script, I'm using it but it have the limitation to be only available for the hero

I am currently working on this kind of script as well, this time, all entities are able to use this ability (enemies, hero, custom entities)

This is not perfect as it might lead to problems currently but it mostly works
Entity's real position remains intact, only their sprites are being moved

Code (lua) Select
local object = {}

-- Falling from ceiling
-- Works with any type of entity
-- Version 0.1 - MetalZelda

--[[
  Make it work:
 
  - Save this script anywhere you want
  - In any script (metatable script is better):
 
  Example: I want to make this available for the hero
  local fall_manager = require("path_to_the_script")
  fall_manager:create("hero")

  Recommended way to make this work:
  -> in main.lua

  local fall_manager = require("path_to_the_script")
  local entities_fall_compatibility = { "hero", "enemy", "npc"}
  for _, entities in ipairs(entities_fall_compatibility) do
    fall_manager:create(entities)
  end
 
 
  Careful, it only work with map entities
 
  To active it, simply call
    - hero:fall_from_ceiling(height, sound, callback) where
 
  height = the height of the falling things (must be positive)
  sound = any sound you wanna play when the falling animation starts
  callback = what to do when it finished (text, cutscene, death, etc)
 
- Remember that you can implement it anywhere else, the target only need to be an entity / sprite
]]

function object:create(meta)
  local object_meta = sol.main.get_metatable(meta)
  local currently_falling = false
 
  function object_meta:fall_from_ceiling(height, sound, callback)
    currently_falling = true

if meta == "hero" then
          -- This means that self returns the hero entity, avoid him from moving.
  self:freeze()
end

-- Get the current object position
local cx, cy, clayer = self:get_position()
local map = self:get_map()

-- Draw a shadow in the entity's real position
local shadow = map:create_custom_entity({
  x = cx,
  y = cy,
  layer = clayer,
  width = 16,
  height = 16,
  sprite = "entities/shadow",
  direction = 0
})


local first_active_sprite = nil

    -- Depending on things, obejct might have different sprite that is synchronized to him
for sprite_name, sprite in self:get_sprites() do
  sprite:set_xy(0, -height)
 
  if first_active_sprite ~= nil then
    first_active_sprite = sprite_name
  end
end

local target_sprite = self:get_sprite(first_active_sprite)

if sound ~= nil then
  sol.audio.play_sound(sound)
end

local movement = sol.movement.create("straight")
movement:set_max_distance(height)
movement:set_angle(3 * math.pi / 2)
movement:set_speed(300)
movement:set_ignore_obstacles(true)
movement:start(target_sprite, function()
  -- Movement finished, disable the falling movement
  first_active_sprite = nil
  currently_falling = false

          sol.audio.play_sound("hero_lands")
 
  if meta == "hero" then
    self:unfreeze()
  end
 
  if callback ~= nil then
    callback()
  end
 
  shadow:remove()
end)

local entity = self

-- Notify the game to synchronize all sprites during the freefall movement if any
function movement:on_position_changed()
  local x, y = target_sprite:get_xy()
 
  local animation = shadow:get_sprite():get_animation()
  local current_height = -y
 
          -- Adding some shadow stuff here

  if current_height == height / 1.5 then
    if animation ~= "demi" then
  shadow:get_sprite():set_animation("demi")
end
  elseif current_height == height / 4 then
    if animation ~= "big" then
  shadow:get_sprite():set_animation("big")
end
  end
 
  -- Depending on things, obejct might have different sprite that is synchronized to him
  for _, sprite in entity:get_sprites() do
    sprite:set_xy(x, y)
  end
 
end
  end
end


return object


This way, you can still use the ability to start the animation on the player when falling from a hole (using map:on_started(destination)), plus you can use it to create falling enemies
Plus, sprites synchronised to an entity will follow the falling movement (hero's shield for example)
You can also make a timer to make the hero to change direction a la ALTTP, there is a local variable that return whever or not the entity is falling or not

Upcoming update will include ground detection, mostly to use a different landing sound depending on the terrain

Nice work ! i'm actually relizing Link was not the only entity that could fall from ceilings (there were the blobs in the Dungeon 6's last rooms, plus the primed bombs dropping with a wrong lever pulling). I don't think i would have been able to make a better version myself, so if you manage to finish your enhancements, then credits shall go to you. 8)

Credits should go to both of you:
PhoenixII54 + MetalZelda = MetalPhoenix  ;D XD
"If you make people think they're thinking, they'll love you. But if you really make them think, they'll hate you."

Sorry for the necropost, but since i will be using MetalZelda's version in a project (the Link's Awakining remake) with some adaptations, i wanted to know if you're okay with us using GPLv3 as a license (with full credits, of course) ?