local ent = ent local scene = entities.currentScene() local metadataJson = ent:getComponent("Metadata") local transform = ent:getComponent("Transform") local physicsBody = ent:getComponent("PhysicsBody") local camera = ent:getComponent("Camera") local cameraTransform = camera:getTransform() local fixedCamera = metadataJson["camera"]["settings"]["fixed"] -- setup all timers local timers = { use = Timer.new(), holp = Timer.new(), flashlight = Timer.new(), physcannon = Timer.new() } if not timers.use:running() then timers.use:start(); end if not timers.holp:running() then timers.holp:start(); end if not timers.flashlight:running() then timers.flashlight:start(); end if not timers.physcannon:running() then timers.physcannon:start(); end -- setup held object locals local heldObject = { uid = 0, distance = 0, smoothSpeed = 4, scrollSpeed = 16, momentum = Vector3f(0,0,0), rotate = false, } -- setup light locals local light = { entity = nil, } for k, v in pairs(ent:getChildren()) do if type(v) == "number" then goto continue end if v:name() == "Light" then light.entity = v end ::continue:: end if light.entity == nil then light.entity = ent:loadChild("./playerLight.json",true) end light.metadata = light.entity:getComponent("LightBehavior::Metadata") light.transform = light.entity:getComponent("Transform") light.power = light.metadata.power light.origin = Vector3f(light.transform.position) light.metadata.power = 0 --light.entity:setComponent("Metadata", { light = { power = 0 } }) -- sound emitter local playSound = function( key, loop ) if not loop then loop = false end local url = "/ui/" .. key .. ".ogg" ent:callHook("sound:Emit.%UID%", { filename = string.resolveURI(url, metadataJson["system"]["root"]), spatial = true, streamed = true, volume = "sfx", loop = loop }, 0) end local stopSound = function( key ) local url = "/ui/" .. key .. ".ogg" ent:callHook("sound:Stop.%UID%", { filename = string.resolveURI(url, metadataJson["system"]["root"]) }, 0) end local useDistance = 6 local pullDistance = useDistance * 4 -- on tick ent:bind( "tick", function(self) local inControl = scene:globalFindByName("Gui: Menu"):uid() == 0 local keyE = inputs.key("E") or inputs.key("R_Y") local keyF = inputs.key("F") local mouse1 = inputs.key("Mouse1") or inputs.key("L_TRIGGER"); local mouse2 = inputs.key("Mouse2") or inputs.key("R_TRIGGER"); local mouse3 = inputs.key("Mouse3"); local wheel = inputs.analog("MouseWheel") if not inControl then keyE = false keyF = false mouse1 = false mouse2 = false mouse3 = false wheel = 0 end -- eye transform local flattenedTransform = nil if fixedCamera then flattenedTransform = transform:flatten() -- flattenedTransform.position.y = flattenedTransform.position.y else flattenedTransform = cameraTransform:flatten() end flattenedTransform.forward = ( transform.forward + Vector3f( 0, cameraTransform.forward.y, 0 ) ):normalize(); -- toggle flashlight if light.enabled then local center = flattenedTransform.position local direction = flattenedTransform.forward * 8 local offset = 0.25 local _, depth = physicsBody:rayCast( center, direction ) if depth >= 0.5 then depth = 0.5 elseif depth < 0 then depth = 0.5 end light.transform.position = center + direction * (depth - offset) end if timers.flashlight:elapsed() > 0.5 and keyF then timers.flashlight:reset() if light.metadata.power ~= light.power then light.metadata.power = light.power light.enabled = true else light.metadata.power = 0 light.enabled = false end playSound("flashlight") end -- fire use ray if timers.use:elapsed() > 0.5 and keyE and inControl then timers.use:reset() local center = flattenedTransform.position local direction = flattenedTransform.forward * useDistance local prop, depth = physicsBody:rayCast( center, direction ) local payload = { user = ent:uid(), uid = prop and prop:uid() or 0, depth = depth, } if prop then prop:lazyCallHook("entity:Use.%UID%", payload) end ent:lazyCallHook("entity:Use.%UID%", payload) end -- update HOLP if heldObject.uid == 0 then if mouse2 then --[[ local center = transform.position + cameraTransform.position local direction = transform.forward + Vector3f( 0, cameraTransform.forward.y, 0 ) direction = direction:normalize() * 4 ]] local center = flattenedTransform.position local direction = flattenedTransform.forward * pullDistance local prop, depth = physicsBody:rayCast( center, direction ) if depth >= 0 and prop and not string.matched( prop:name(), "/^worldspawn/" ) then local heldObjectTransform = prop:getComponent("Transform") local heldObjectPhysicsState = prop:getComponent("PhysicsBody") local strength = 500 local distanceSquared = (heldObjectTransform.position - flattenedTransform.position):magnitude() heldObjectPhysicsState:applyImpulse( flattenedTransform.forward * -heldObjectPhysicsState:getMass() * strength / distanceSquared ) if timers.physcannon:elapsed() > 1.0 then timers.physcannon:reset() playSound("phys_tooHeavy") end end end else if wheel ~= 0 then heldObject.distance = heldObject.distance + (wheel / 120 * heldObject.scrollSpeed) * time.delta() end if mouse3 then heldObject.rotate = not heldObject.rotate end local prop = entities.get( heldObject.uid ) local heldObjectTransform = prop:getComponent("Transform") local heldObjectPhysicsState = prop:getComponent("PhysicsBody") if mouse1 and timers.physcannon:elapsed() > 0.5 then timers.physcannon:reset() heldObject.uid = 0 heldObjectPhysicsState:enableGravity(true) heldObjectPhysicsState:applyImpulse( flattenedTransform.forward * heldObjectPhysicsState:getMass() * 50 ) playSound("phys_launch"..math.random(1,4)) else if heldObject.rotate then heldObjectTransform.orientation = Quaternion.lookAt( (heldObjectTransform.position - flattenedTransform.position):normalize(), transform.up ) end local forward = flattenedTransform.forward * heldObject.distance --flattenedTransform.orientation:rotate( Vector3f(0,0,1) ) if heldObject.smoothSpeed ~= 0 then local target = flattenedTransform.position + forward local offset = target - heldObjectTransform.position local delta = offset * time.delta() * heldObject.smoothSpeed local distance = delta:norm() if distance > 0.001 then if timers.holp:elapsed() > 0.125 then timers.holp:reset() heldObjectPhysicsState:setVelocity( delta * 20 ) end else heldObjectPhysicsState:setVelocity( Vector3f(0,0,0) ) end else heldObjectTransform.position = flattenedTransform.position + forward end end end end ) -- on use ent:addHook( "entity:Use.%UID%", function( payload ) if payload.user ~= ent:uid() then return end local validUse = false if heldObject.uid == 0 and payload.depth > 0 then local prop = entities.get( payload.uid ) local propMetadata = prop:getComponent("Metadata") if propMetadata["holdable"] then validUse = true local offset = transform.position - prop:getComponent("Transform").position heldObject.uid = payload.uid heldObject.distance = offset:norm() prop:getComponent("PhysicsBody"):enableGravity(false) print( prop:name() ) else validUse = not string.matched( prop:name(), "/^worldspawn/" ) end elseif heldObject.uid ~= 0 then validUse = true local prop = entities.get( heldObject.uid ) local heldObjectPhysicsState = prop:getComponent("PhysicsBody") heldObjectPhysicsState:enableGravity(true) heldObjectPhysicsState:applyImpulse( heldObject.momentum ) heldObject.uid = 0 heldObject.distance = 0 heldObject.momentum = Vector3f(0,0,0) end if validUse then playSound("select") else playSound("deny") end end )