engine/bin/data/entities/scripts/door.lua

214 lines
5.2 KiB
Lua

local ent = ent
local scene = entities.currentScene()
local controller = entities.controller()
local timer = Timer.new()
if not timer:running() then
timer:start()
end
local state = 0
local currentDistance = 0
local polarity = 1
local transform = ent:getComponent("Transform")
local physicsBody = ent:getComponent("PhysicsBody")
local metadata = ent:getComponent("Metadata")
local metadataDoor = metadata["door"] or {}
local speed = metadataDoor["speed"] or 100.0
local wait = metadataDoor["wait"] or 4.0
local isRotating = (metadataDoor["axis"] ~= nil)
local targetDistance = 0
local moveDir = Vector3f(0, 0, 0)
local rotAxis = Vector3f(0, 1, 0)
local flags = metadataDoor["spawnflags"] or 0
local isToggle = (math.floor(flags / 32) % 2) ~= 0
if isToggle then
wait = -1
end
if isRotating then
local ax = metadataDoor["axis"]
rotAxis = Vector3f(ax[1], ax[2], ax[3]):normalize()
local dist = metadataDoor["distance"] or 90.0
local isReverse = (math.floor(flags / 2) % 2) ~= 0
if isReverse then
polarity = -1
end
polarity = polarity * -1
if dist < 0 then
polarity = polarity * -1
dist = math.abs(dist)
end
targetDistance = math.rad(dist)
speed = math.rad(speed)
else
local dir = metadataDoor["direction"]
if dir then moveDir = Vector3f(dir[1], dir[2], dir[3]):normalize() end
local lip = metadataDoor["lip"] or 8.0
if physicsBody:initialized() then
local obb = OBB(physicsBody:bounds())
local size = obb.extent * 2.0
local travelSize = math.abs(moveDir:dot(size))
targetDistance = travelSize - lip
else
targetDistance = 96.0 - lip
end
end
local soundEmitter = ent
local playSound = function( key, loop )
if not loop then loop = false end
local url = "/door/" .. key .. ".ogg"
soundEmitter:queueHook("sound:Emit.%UID%", {
filename = string.resolveURI(url, metadata["system"]["root"]),
spatial = true, streamed = true, volume = "sfx", loop = loop
}, 0)
end
local function toggleDoor( payload )
if state == 0 or state == 3 then
state = 1
playSound("default_move")
if isRotating and payload.uid ~= nil then
local isOneWay = (math.floor(flags / 16) % 2) ~= 0
if not isOneWay then
local user = entities.get( payload.user )
if user and physicsBody:initialized() then
local userPos = user:getComponent("Transform").position
local obb = OBB(physicsBody:bounds())
local doorCenter = obb.center
local hingePos = transform.position
local doorBlade = doorCenter - hingePos
local doorNormal = rotAxis:cross(doorBlade):normalize()
local delta = userPos - doorCenter
if doorNormal:dot(delta) > 0 then
polarity = 1
else
polarity = -1
end
local baseDist = metadataDoor["distance"] or 90.0
if baseDist < 0 then
polarity = polarity * -1
end
end
end
end
elseif state == 2 then
state = 3
playSound("default_move")
end
end
-- on tick
ent:bind( "tick", function(self)
local deltaMove = 0
local step = time.delta() * speed
if state == 1 then
deltaMove = step
currentDistance = currentDistance + step
if currentDistance >= targetDistance then
deltaMove = deltaMove - (currentDistance - targetDistance)
currentDistance = targetDistance
state = 2
timer:reset()
playSound("default_stop")
ent:queueHook("io:FireOutput.%UID%", { output = "OnOpen" }, 0)
end
elseif state == 3 then
deltaMove = -step
currentDistance = currentDistance - step
if currentDistance <= 0 then
deltaMove = deltaMove - currentDistance
currentDistance = 0
state = 0
playSound("default_stop")
end
elseif state == 2 and wait >= 0 then
if timer:elapsed() >= wait then
state = 3
playSound("default_move")
end
end
if deltaMove ~= 0 then
local finalMove = deltaMove * polarity
if isRotating then
local rot = Quaternion.axisAngle(rotAxis, finalMove)
if physicsBody:initialized() then
physicsBody:applyRotation(rot)
else
transform:rotate(rot)
end
else
local vec = moveDir * finalMove
if physicsBody:initialized() then
print("Moving by: ", finalMove, " Axis: ", moveDir.x, moveDir.y, moveDir.z)
physicsBody:setVelocity(moveDir * (finalMove / time.delta()))
else
transform.position = transform.position + vec
end
end
else
if not isRotating and physicsBody:initialized() then
physicsBody:setVelocity(Vector3f(0,0,0))
end
end
end )
-- on use
ent:addHook( "entity:Use.%UID%", function( payload )
if payload.user == ent:uid() then return end
local useOpens = (math.floor(flags / 256) % 2) ~= 0
if useOpens then
toggleDoor( payload )
ent:queueHook("io:FireOutput.%UID%", { output = "OnUse" }, 0)
else
playSound("default_locked")
end
end )
ent:addHook("io:Input.%UID%", function( payload )
local input = payload.input
print(ent:name() .. " received I/O Input: " .. input)
local mockPayload = {
user = payload.caller,
uid = ent:uid()
}
if input == "Open" and (state == 0 or state == 3) then
toggleDoor(mockPayload)
elseif input == "Close" and (state == 2 or state == 1) then
toggleDoor(mockPayload)
elseif input == "Toggle" or input == "Use" then
toggleDoor(mockPayload)
end
end)