Hi !
Have you ever wanted to have those pipes in Crystal Palace of MoS DX move your characters automagically ? Then this script is for you !
--A simple string splitter, found on the Internet.
--Used for extracting parameters in the sensor name.
--You might want to use your own version to avoid possible license issues
local function string_split(source, delimiters)
local elements = {}
local pattern = '([^'..delimiters..']+)'
string.gsub(source, pattern, function(value)
elements[#elements + 1] = value
end)
return elements
end
--A simple yet powerful pipe system like in A link to the Past Turtle Rock
--Usage : Place a series of sensors on the map, then name them as this :
--pipe_<path_id>_<path_index>
--Warning : you MUST set the indexes to a continuous way (1,2,3,...) or else you will be stuck in the middle of the movement.
--Notice : While it requires nothing but requiring this script in the main file to run, you might want to use en event register like in Christopho's projects
--as it overwrites the sensors' on_activated event
local sensor_meta=sol.main.get_metatable("sensor")
function sensor_meta:on_activated()
local name=self:get_name()
local map=self:get_map()
local hero=map:get_hero()
if name~=nil then
local type=string_split(name,"_")
if type[1]=="pipe" then
--We are now in the pipe, either on en end or in the middle of it.
if not sensor_meta.pipe_active then
--Actually entering the pipe
length=map:get_entities_count("pipe_"..type[2])
hero:freeze()
hero:set_visible(false)
--detecting whether we are going in the increasing index order or not
local reversed=(tonumber(type[3])==length)
local next
if reversed then
next=length-1
else
next=2
end
--Saving settings in the metatable for later usage
sensor_meta.pipe_next=next
sensor_meta.pipe_is_reversed=reversed
sensor_meta.pipe_length=length
sensor_meta.pipe_active=true
else
--Already in the pipe and continuing the movement
if sensor_meta.pipe_is_reversed then
sensor_meta.pipe_next=sensor_meta.pipe_next-1
else
sensor_meta.pipe_next=sensor_meta.pipe_next+1
end
end
--Actually start the movement
if sensor_meta.pipe_next==0 or sensor_meta.pipe_next==sensor_meta.pipe_length+1 then
--We are about to finish the move : make the hero walk out of the pipe
local movement=sol.movement.create("path")
local dir=sensor_meta.pipe_outDir or 0
local path={dir,dir}
movement:set_path(path)
hero:set_visible(true)
hero:set_direction(dir/2)
hero:set_animation("walking")
movement:start(hero,function()
hero:unfreeze()
sensor_meta.pipe_active=false
end)
else
--making the quick auto move in the pipe (and bonk in the corners)
--Feel free to change this sound to your own !
sol.audio.play_sound("bomb")
local movement=sol.movement.create("target")
local target="pipe_"..type[2].."_"..sensor_meta.pipe_next
movement:set_target(map:get_entity(target))
movement:set_speed(192)
movement:start(hero)
--Saving the movement direction for when we will leave the pipe
sensor_meta.pipe_outDir=2*movement:get_direction4()
end
end
end
--And that(s all folks !
end
Note: if you find a simpler way, don't hesitate to tell me, i'll be happy to rework my code as a challenge !
Enjoy !
edit : correction of misnamed function call originally using another file.
edit #2 : correction of movement:set_garget :
It used map:get_entities(target)(1), now it uses map:set_entity(target) since now you should have ensured all sensors have a unique ID.
edit #3 : fixed a possible issue when you try to make a pipe with only 1 sensor.
+ fixed comments indentation, removed some typo and removed remaining debug prints.
A different way (not necessarily better or worse) is to use streams that freeze the hero, as conveyor belts, and two sensors to start/finish the walking animations (at both sides of the pipe). My approach requires more mapping, though.
That code seems very interesting, for learning purposes, for people who is learning Lua or want to improve skills on coding (and looks very clean, short and polished). Also, I love that word: "automagically" ;D
You know what ? that's what i did in a first version with custom entities instead of the sensors, it worked well too, but wanted something "universal", as in just place any number of sensors carefully named and voila!
Anyways, thanks!
movement:set_target(map:get_entities(target)(1))
Wait such thing is possible ?
Hum, map:get_entities(prefix) returns an iterator so you can indeed get its first value by calling the result:
local entity = map:get_entities(prefix)()
But this is obscure to read, and the parameter 1 is useless and has no effect.
Actually this is a reminiscence of an experimental way where i used the name to code the output direction when at the end of a pipe, which caused problems since i could''t get the next entity if i was at the very start of the chain, but since then i found that using the direction of the actual movement is way easier, so you can replace it by
map:get_entity(target)
since the sensors now use the same name convention.
edit : code is now updated to apply the modification.