Scripts to allow custom jump over custom teletransporters

Started by Diarandor, December 18, 2015, 01:06:34 PM

Previous topic - Next topic
December 18, 2015, 01:06:34 PM Last Edit: December 19, 2015, 07:32:39 AM by Diarandor
Hi! I made scripts to replace built-in teletransporters by custom entities (custom teletransporters). This is done using a function when the map has changed, which replaces automatically all built-in teletransporters of the map by the custom ones.

The point of this is that custom teletransporters, unlike the built-in ones, can interrupt teleportation if some condition is satisfied, for instance if the hero is jumping (usually over them). So this allows jumping over teletransporters, which would not be possible otherwise because built-in teleporters cannot be aborted at present. (Remark: I am using a custom jump, not the built-in one.) The scripts are very easy to use.

This is the script for the custom entity teleporter (entities/teleporter.lua):
Code (Lua) Select

--[[
Custom entity to teleport the hero to other map. These entities, unlike the built-in
teletransporters, allow to abort the teleportation if some condition is satisfied.
In our case, we abort the teleportation if the hero is jumping over the teleporter.
Important: in all the maps we create usual built-in teletransporters, to ease the creation
of teletransporters in the editor. Then, a scripted function defined in the map metatable
is used at runtime to replace built-in nonscrolling teletransporters with custom teleporters
when the map is loaded. The jumping state of the hero must be a boolean stored in hero.is_jumping.
--]]

local entity = ...

local sound_id, transition_style, destination_map, destination_name

function entity:on_created()
end

function entity:teleport()
  if self.on_activated then self:on_activated() end -- Teleporting event.
  local hero = self:get_map():get_hero()
  -- Repair destination name obtained from side scrolling teletransporters.
  if destination_name == "_side" then
    local dir = (hero:get_direction()+2)%4
    destination_name = "_side" .. dir
  end
  -- Teleport.
  hero:teleport(destination_map, destination_name, transition_style)
end

function entity:set_sound(sound_id) sound_id = sound_id end
function entity:get_sound() return sound_id end
function entity:set_transition(transition) transition_style = transition end
function entity:get_transition() return transition_style end
function entity:set_destination_map(map_id) destination_map = map_id end
function entity:get_destination_map() return destination_map end
function entity:set_destination_name(destination) destination_name = destination end
function entity:get_destination_name() return destination_name end


And this is the main script (scripts/.../custom_teleporters.lua) which defines the replacing function:
Code (Lua) Select

--[[
Function to replace built-in teletransporters with custom teleporters.
This is called when the maps are loaded. Custom teleporters, unlike the built-in
teletransporters, allow to abort the teleportation if some condition is satisfied.
In our case, we abort the teleportation if the hero is jumping, so we avoid the use
of the teleporter if the hero is jumping overt it.
Important: in all the maps we create usual built-in teletransporters, to ease the creation
of teletransporters in the editor. Then we replace the non-scrolling ones with custom
teleporters.
---
Use:
1)Load the file from the game manager or other script using.
write sol.main.load_file("scripts/.../custom_teleporters.lua")(game)
2) The function game:customize_teleporters() must be called when loading any map.
This can be done using the event game:on_map_changed(map).
3) Change the value of the variable hero.is_jumping to true/false
when the hero starts/ends jumping, so that the custom teleporters can
know if the hero is jumping or not.
--]]

local game = ...

function game:customize_teleporters()
  local map = game:get_map()
  -- Replace non-scrolling teletransporters.
  for e in map:get_entities("") do
    if e:get_type() == "teletransporter" then
      -- Get general properties.
      local x,y,layer = e:get_position()
      local name = e:get_name()
      local enabled = e:is_enabled()
      local origin_x, origin_y = e:get_origin()
      local w, h = e:get_size()
      -- Get teletransporter properties.
      local on_activated = e.on_activated
      local sprite = e:get_sprite()
      local animation, animation_set
      if sprite then
        animation_set = sprite:get_animation_set()
        animation = sprite:get_animation()
      end   
      local sound_id = e:get_sound()
      local destination_map = e:get_destination_map()
      local destination_name = e:get_destination_name()
      local transition = e:get_transition()
      -- Replace teletransporter with custom entity.
      e:remove()
      local properties = {name = name, x = x, y = y, layer = layer,
        direction = 0, model = "teleporter"}
      local t = map:create_custom_entity(properties)
      t:set_enabled(enabled)
      t:set_origin(origin_x, origin_y)
      t:set_size(w, h)
      -- Mimic initial properties.
      t.on_activated = on_activated -- On activated event.
      if animation_set then
        local new_sprite = t:create_sprite(animation_set)
        new_sprite:set_animation(animation)
      end
      t:set_sound(sound_id)
      t:set_destination_map(destination_map)
      t:set_destination_name(destination_name)
      t:set_transition(transition)
      -- Add teleportation collision test with hero. This requires the hero not to be jumping.
      local test = "center"
      if transition == "scrolling" then test = "facing" end
      t:add_collision_test(test, function(teleporter, other)
        if other:get_type() == "hero" then         
          -- Condition to teleport: hero is not jumping.
          if not other.is_jumping then
            t:teleport() -- Activate teleportation.
          end   
        end
      end)

    end
  end
end


It's tested and works perfectly. Usage explanations can be found in the comments of these scripts.
I hope someone finds this useful.

EDIT: updated to solve a small problem.
"If you make people think they're thinking, they'll love you. But if you really make them think, they'll hate you."

October 05, 2016, 04:20:42 PM #1 Last Edit: October 05, 2016, 04:40:03 PM by Diarandor
This is my last version of the custom teleporter script, that uses custom entities as teleporters.
Code (Lua) Select

--[[
Custom entity to teleport the hero to other map. These entities, unlike the built-in
teletransporters, allow to abort the teleportation if some condition is satisfied.
In our case, we abort the teleportation if the hero is jumping over the teleporter.
Important: in all the maps we create usual built-in teletransporters, to ease the creation
of teletransporters in the editor. Then, a scripted function defined in the map metatable
is used at runtime to replace built-in nonscrolling teletransporters with custom teleporters
when the map is loaded. The jumping state of the hero must be a boolean stored in hero.is_jumping.
--]]

local entity = ...
local map = entity:get_map()
local game = map:get_game()
local hero = map:get_hero()

local sound_id, transition_style, destination_map, destination_name

function entity:on_created()
end

function entity:teleport()
  if self.on_activated then self:on_activated() end -- Teleporting event.

  -- If there is bad ground under the hero, abort teleportation.
  local ground = map:get_ground(self:get_position())
  if ground == "hole" or ground == "lava" or
    ( (not game:has_ability("swim")) and
      ground == "deep_water"
      or ground == "wall_top_right_water"
      or ground == "wall_top_left_water"
      or ground == "wall_bottom_right_water"
      or ground == "wall_bottom_left_water"
    )
  then
    return
  end
 
  -- Repair destination name obtained from side scrolling teletransporters.
  if destination_name == "_side" then
    --local dir = (hero:get_direction()+2)%4
    local x,y,_ = self:get_position()
    local w,h = self:get_map():get_size()
    local dir = nil
    if x < 0 then dir = 0 elseif x >= w then dir = 2
    elseif y < 0 then dir = 3 elseif y >= h then dir = 1
    end
    if dir == nil then
      -- A scrolling teleporter must be on the border of the map!
      error("Wrong position for custom scrolling teleporter"); return 
    end
    destination_name = "_side" .. dir
  end

  -- Teleport.
  hero:teleport(destination_map, destination_name, transition_style)
end

function entity:set_sound(new_sound_id) sound_id = new_sound_id end
function entity:get_sound() return sound_id end
function entity:set_transition(transition) transition_style = transition end
function entity:get_transition() return transition_style end
function entity:set_destination_map(map_id) destination_map = map_id end
function entity:get_destination_map() return destination_map end
function entity:set_destination_name(destination) destination_name = destination end
function entity:get_destination_name() return destination_name end
"If you make people think they're thinking, they'll love you. But if you really make them think, they'll hate you."