Automatic Elevator Script
Posted: Sun Oct 14, 2012 9:15 pm
EDIT:
Version 2 is now available with several enchancements including only one script to move around.
viewtopic.php?f=14&t=3723#p38402
ORIGINAL:
I hesitated to post this because it's already obsolete in my book because I thought of a much better way to implement this. However, since it works and I spent so much time on it, I'll post it and hopefully add version 2 soon.
This is an automatic elevator script that builds a one by one elevator (sans good graphics so far, sorry). Download and define the custom sounds, take the script, edit its operational parameters, create a stacked one by one alcove on every floor the elevator is to service, and paste the script on each floor (read the usage section for more information). That's it. You'll have a working elevator connecting all the floors. Note my LED Display: 0-99 script is embedded and can be removed if the floor display is not wanted. I defined a few objects to place in the elevator to make it look a little better, but it really needs custom graphics, I think. I wanted to get the mechanics working and hope for better graphics later.
Please post a link here to your dugneon if you used this code anywhere. I'd really like to see your work! I'd also like to know about any enhancement requests you might have or any assistence you could lend with custom graphics.
Thank you, -Lark
IMAGE:
DOWNLOADS: (save target as...)
https://ntg.missouristate.edu/images/lark/elevator.zip
VERSION 1:
Version 2 is now available with several enchancements including only one script to move around.
viewtopic.php?f=14&t=3723#p38402
ORIGINAL:
I hesitated to post this because it's already obsolete in my book because I thought of a much better way to implement this. However, since it works and I spent so much time on it, I'll post it and hopefully add version 2 soon.
This is an automatic elevator script that builds a one by one elevator (sans good graphics so far, sorry). Download and define the custom sounds, take the script, edit its operational parameters, create a stacked one by one alcove on every floor the elevator is to service, and paste the script on each floor (read the usage section for more information). That's it. You'll have a working elevator connecting all the floors. Note my LED Display: 0-99 script is embedded and can be removed if the floor display is not wanted. I defined a few objects to place in the elevator to make it look a little better, but it really needs custom graphics, I think. I wanted to get the mechanics working and hope for better graphics later.
Please post a link here to your dugneon if you used this code anywhere. I'd really like to see your work! I'd also like to know about any enhancement requests you might have or any assistence you could lend with custom graphics.
Thank you, -Lark
IMAGE:
SpoilerShow
https://ntg.missouristate.edu/images/lark/elevator.zip
VERSION 1:
Code: Select all
---------------------------------------------------------------------------------------
--- Automatic Elevator: Version: 1.0.1 Written by Lark, 10-1-2012 ---
--- Updated: 10-11-2012 Mark.Harsen@missouriState.edu ---
-- Special Thanks to: Lmaoboat and BeNNyBiLL ---
---------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------
--- Parameters: Set carefully for proper operation! ---
---------------------------------------------------------------------------------------
car_tracking = true --true to set realistic car arrival times and sounds; false for instantly available
anchor_script = "script_ele_1" --if car tracking, elevator anchor script name where car starts; must exist once; referenced by all elevators in the stack
floor_display = self.level --nil or the numeric value to display on the back wall of the elevator
upper_limit = 1 --the highest level serviced; don't look for an elevator above this
lower_limit = 25 --the lowest level serviced; don't look for an elevator below this (tried to make this automatic)
auto_close = true --true if the door closes automatically after the elevator arrives or after it is triggered open
initially_opened = false --specifies if the door is initially opened or not
floor_delay = 3 --delay in seconds the car must travel per floor; final floor is always 2 seconds or more to allow for arrival sound
call_by_button = {1,0,0} --nil for call by plate or {left-offset, depth-offset, rotation-offset} from in front of door; example placements:
--to left {1,0,0}; to right {-1,0,0}, right facing {0,0,1}; opposite door {0,0,2}; left facing {0,0,3}; behind {0,-3,2}
---------------------------------------------------------------------------------------
--- Usage Section: ---
---------------------------------------------------------------------------------------
--
-- 1. Download and define custom sounds
-- 2. Set parameters as desired for a given stack of an elevator. You may skip step 3 if elevator car tracking is not to occur
-- 3. Set the upper_limit and lower_limit to the top floor and bottom floor serviced by the elevator (optional but recommended)
-- 4. Pick the name of one of the scripts (i.e. the first) and set "anchor_script" to this name - the elevator car will start here
-- 5. Create a one by one square "alcove" with only one side open, the same side, on every floor to serviced by the elevator
-- 6. Manually set graphics for elevator walls, ceiling, and floor - the script can't do this and replace the existing graphics.
-- 7 Place the modifiec version of this script in every alcove on every floor facing the opening - (version 2 will be a single script!)
-- 8. All should face the same direction for a given elevator. To create multiple elevators, use a different "anchor_script" name.
-- 9. Change the "call_by_button" field in each script to place the call button on each floor as desired. nil = call by pressure plate.
-- 10. That should be it. The outside button calls the elevator, the inside button moves the car in the direction of the lever.
---------------------------------------------------------------------------------------
--- Function Section: ---
---------------------------------------------------------------------------------------
---
--- round to nearest integer
---
function round(num)
local floor = math.floor(num)
local ceiling = math.ceil(num)
if (num - floor) >= 0.5 then
return ceiling
end
return floor
end
---
--- Remember what floor our elevator is on - used to delay calls if appropriate (called remotely)
---
function carLocation(n)
if n ~= nil then carFloor = n end
return carFloor
end
---
--- Call the elevator
---
function elevatorCall()
if callIsActive then return end
if anchor.carLocation() == self.level or not car_tracking then
remoteOpen()
return
end
callIsActive = true
wait = math.max(0, math.abs(anchor.carLocation() - self.level) * floor_delay - 2)
timerGone:setTimerInterval(wait)
timerGone:activate()
end
---
--- Elevator Just arrived on our floor: play bell and open door
---
function justArrived()
timerGone:deactivate()
playSound("elevatorRun2")
timerNear:activate()
end
---
--- Our inside button closes the door, waits, then starts elevatorMove()
---
function doorClose()
if elevatorIsActive then
playSound("elevatorBuzz")
return
end
elevatorIsActive = true
if lever:getLeverState() == "activated"
then dir = 1
else dir = -1
end
if self.level + dir < upper_limit or self.level + dir > lower_limit then
playSound("elevatorBuzz")
elevatorIsActive = false
return
end
--Find out if an elevator (any script for now) exists in the desired direction of travel
IsPartnerOkay = false
for entity in entitiesAt(self.level + dir, self.x, self.y) do
if entity.name == "script_entity" then
if door:isClosed() then
--door:setDoorState("closed") --may not really be closed yet, so force it. [doesn't work]
elevatorMove()
return
end
door:close()
timerClose:activate()
IsPartnerOkay = true
break
end
end
if IsPartnerOkay == false then
playSound("elevatorBuzz")
elevatorIsActive = false
end
end
---
--- Go up or down based on the lever, play elevator sound, shake car, and schedule elevatorBell()
---
function elevatorMove()
timerClose:deactivate()
mover:setTeleportTarget(self.x, self.y, 0, self.level + dir)
playSound("elevatorRun4")
party:shakeCamera(.02,4.3)
timerRide:activate()
end
---
--- Deactivate timerRide timer, duplicate lever state, signal arrival, teleport the party, and activate open timer
---
function elevatorArrive()
timerRide:deactivate()
for entity in entitiesAt(party.level + dir, party.x, party.y) do
if entity.name == "script_entity" then
entity.remoteLever(lever:getLeverState())
break
end
end
playSound("elevatorBell")
mover:activate()
timerOpen:activate()
end
---
--- Open the door above or below our original location, deactivate the teleporter, and stop last timer
---
function elevatorOpen()
timerOpen:deactivate()
mover:deactivate()
for entity in entitiesAt(party.level, party.x, party.y) do
if entity.name == "script_entity" then
entity:remoteOpen()
break
end
end
elevatorIsActive = false
end
---
--- Open the door where the party was teleported (called locally and remotely)
---
function remoteOpen()
timerNear:deactivate()
anchor.carLocation(self.level)
if callIsActive and wait ~= 0 then
playSound("elevatorBell")
callIsActive = false
end
door:open()
if auto_close then timerHere:activate() end
end
---
--- Close the door again
---
function localClose()
timerHere:deactivate()
callIsActive = false
door:close()
end
---
--- Position the level where the party will be teleported to the same as our starting location
---
function remoteLever(state)
lever:setLeverState(state)
end
---------------------------------------------------------------------------------------
--- Global Code Section: ---
---------------------------------------------------------------------------------------
---
--- Set up car tracking; find our anchor script
---
anchor = findEntity(anchor_script)
if anchor == nil then
if car_tracking then hudPrint(self.id.." says: anchor not found; car tracking force disabled.") end
car_tracking = false
anchor = findEntity(self.id)
end
if anchor.id == self.id then carFloor = self.level end
---
--- Set up standard door, inside button, and directional lever
---
os = {{0,-1}, {1,0}, {0,1}, {-1,0}} --[NOTE: change offset method to getForward...]
door = spawn("dungeon_door_iron", self.level, self.x + os[self.facing + 1][1], self.y + os[self.facing + 1][2], (self.facing+2)%4, "ele_door"..self.id)
if initially_opened then door:setDoorState("open") end
button = spawn("wall_button", self.level, self.x, self.y, (self.facing+1)%4, "ele_button"..self.id)
button:addConnector("toggle", self.id, "doorClose")
lever = spawn("lever", self.level, self.x, self.y, (self.facing+3)%4, "ele_lever"..self.id)
---
--- Set up call button or call pressure plate
---
if call_by_button == nil then
plate = spawn("pressure_plate_hidden", self.level, self.x + os[self.facing + 1][1], self.y + os[self.facing + 1][2], 0, "ele_plate"..self.id)
plate:setTriggeredByParty(true)
plate:addConnector("activate", self.id, "elevatorCall")
else
local cos2 = round(math.cos(1.5707963267949 * self.facing))
local sin2 = round(math.sin(1.5707963267949 * self.facing))
local r1 = self.x + os[self.facing + 1][1] +(call_by_button[1] * cos2 - call_by_button[2] * sin2 * (1 - self.facing % 2 * 2))
local r2 = self.y + os[self.facing + 1][2] + (call_by_button[1] * sin2 - call_by_button[2] * cos2)
callButton = spawn("wall_button", self.level, r1, r2, (call_by_button[3] + self.facing + 2) % 4)
callButton:addConnector("toggle", self.id, "elevatorCall")
end
--Can't be spawned else it doesn't replace existing walls
--spawn("elevator_plating", self.level, self.x, self.y, (self.facing+1)%4)
--spawn("elevator_plating", self.level, self.x, self.y, (self.facing+2)%4)
--spawn("elevator_plating", self.level, self.x, self.y, (self.facing+3)%4)
--spawn("elevator_floor", self.level, self.x, self.y, self.facing)
---
--- Set up elevator timers to delay code execution between various events
---
-- Delay while closing the elevator door, then start the ride
timerClose = spawn("timer", self.level, self.x, self.y, 0, "doorWait"..self.id)
timerClose:setTimerInterval(2)
timerClose:addConnector("activate", self.id, "elevatorMove")
-- Delay while riding between floors, then start arrival sequence
timerRide = spawn("timer", self.level, self.x, self.y, 0, "timerRide"..self.id)
timerRide:setTimerInterval(4)
timerRide:addConnector("activate", self.id, "elevatorArrive")
-- Delay just enough for the teleport to complete, then trigger door opening and reset
timerOpen = spawn("timer", self.level, self.x, self.y, 0, "timerOpen"..self.id)
timerOpen:setTimerInterval(0.1)
timerOpen:addConnector("activate", self.id, "elevatorOpen")
-- Elevator is here, delay with door open for awhile, then close
timerHere = spawn("timer", self.level, self.x, self.y, 0, "timerHere"..self.id)
timerHere:setTimerInterval(11)
timerHere:addConnector("activate", self.id, "localClose")
-- Elevator is elsewhere, delay with door closed for awhile, then open
timerGone = spawn("timer", self.level, self.x, self.y, 0, "timerGone"..self.id)
timerGone:addConnector("activate", self.id, "justArrived")
-- Elevator is arriving on our floor, delay while sounds play
timerNear = spawn("timer", self.level, self.x, self.y, 0, "timerNear"..self.id)
timerNear:setTimerInterval(2)
timerNear:addConnector("activate", self.id, "remoteOpen")
---
--- Define the teleporter that will move us between floors and initialize variables
---
mover = spawn("teleporter", self.level, self.x, self.y, 0, "mover"..self.id)
mover:setChangeFacing(false)
mover:setInvisible(true)
mover:setHideLight(true)
mover:setSilent(true)
mover:setScreenFlash(false)
elevatorIsActive = false
callIsActive = false
wait = 0
---------------------------------------------------------------------------------------
--- LED display: version 1.4.1 10-10-2012 Written by Lark ---
--- Much thanks to: Lmaoboat ---
---------------------------------------------------------------------------------------
---
--- Light Parameters
---
displayed = floor_display --the initial number displayed by the script
wall = (self.facing + 2) % 4 --adjust wall used for display from script's facing; i.e. opposite = (self.facing + 2) % 4
initallyOn = true --specify if the light is initially on
vertical_spacing = .17 --vertical LED spacing
horizontal_spacing = .15 --horizontal LED spacing; center, left, & right values are based on this number being .15
height = 3 --height above the floor for the top row of LEDs
center = -.3 --start of row offset for single digit displays
left = -.85 --start of row offset for left digit
right = .25 --start of row offset for right digit
--- Usage:
---
--- 1. drop this script into a square facing the wall where the display is desired (or adjust "wall" above)
--- 2. set parameters to suit: displayed, wall, and initiallyOn for basic parameters
--- 3. on() to display the initial value
--- 4. off() to turn off lights
--- 5. add() to increment by one
--- 6. sub() to decrement by one
--- 7. display(number) to set the display to a new number
--- 8. do not call digit() directly
---
--- Create a LED panel for numbers 0 - 99. LED grid is:
---
-- 00 01 02 03 04
-- 05 06 07 08 09
-- 10 11 12 13 14
-- 15 16 17 18 19
-- 20 21 22 23 24
-- 25 26 27 28 29
-- 30 31 32 33 34
digits = {
{1,2,3,5,9,10,14,15,19,20,24,25,29,31,32,33},
{6,2,7,12,17,22,27,31,32,33},
{5,1,2,3,9,14,18,22,26,30,31,32,33,34},
{5,1,2,3,9,14,18,17,16,24,29,33,32,31,25},
{0,5,10,15,16,17,18,19,3,8,13,23,28,33},
{4,3,2,1,0,5,10,15,16,17,18,24,29,33,32,31,30},
{9,3,2,1,5,10,15,20,25,31,32,33,29,24,18,17,16},
{5,0,1,2,3,4,9,14,18,22,27,32},
{10,5,1,2,3,9,14,18,17,16,20,25,31,32,33,29,24},
{25,31,32,33,29,24,19,14,9,3,2,1,5,10,16,17,18}}
led = {nil}
cos = math.cos(1.5707963267949 * wall)
sin = math.sin(1.5707963267949 * wall)
tz = 1.3
---
--- divide into digits and display in correct sector
---
function display(number)
if number == nil then return end
local b = number % 10
local a = (number - b) / 10
off()
if a > 0 then
digit(a, left)
digit(b, right)
else
digit(b, center)
end
displayed = number
end
---
--- display a digit of the number at the given alignment and light set
---
function digit(number, align)
local dix = number + 1
for ix = 1, 35 do
local pix = digits[dix][ix]
if pix == nil then return end
local tx = align + pix % 5 * horizontal_spacing
lix = lix + 1
led[lix] = spawn("fx", self.level, self.x, self.y, 3, self.id..lix)
led[lix]:setLight(1, 0, 0, 1500, 0.24, 360000, false)
led[lix]:translate(tx * cos + tz * sin, height - math.floor(pix / 5) * vertical_spacing - 1, tz * cos + tx * sin * (1 - wall%2 * 2))
end
end
---
--- add one to the current display w/ wrap
---
function add()
display((displayed + 1)%100)
end
---
--- subtract one from the current display w/ wrap
---
function sub()
display((displayed + 99)%100)
end
---
--- turn lights off
---
function off()
if lix ~= null then
for dix = 1, lix do
led[dix]:destroy()
end
end
lix = 0
led = {nil}
end
---
--- turn lights on
---
function on()
display(displayed)
end
---
--- turn lights on if specified in parameters
---
if initallyOn then on() end