diff --git a/leenkx/Sources/leenkx/trait/FirstPersonController.hx b/leenkx/Sources/leenkx/trait/FirstPersonController.hx index 692868e..8f65e13 100644 --- a/leenkx/Sources/leenkx/trait/FirstPersonController.hx +++ b/leenkx/Sources/leenkx/trait/FirstPersonController.hx @@ -1,87 +1,243 @@ package leenkx.trait; +import iron.Trait; import iron.math.Vec4; import iron.system.Input; import iron.object.Object; import iron.object.CameraObject; import leenkx.trait.physics.PhysicsWorld; -import leenkx.trait.internal.CameraController; +import leenkx.trait.physics.RigidBody; +import kha.FastFloat; -class FirstPersonController extends CameraController { +class FirstPersonController extends Trait { -#if (!lnx_physics) - public function new() { super(); } -#else + #if (!lnx_physics) + public function new() { super(); } + #else - var head: Object; - static inline var rotationSpeed = 2.0; + @prop public var rotationSpeed:Float = 0.15; + @prop public var maxPitch:Float = 2.2; + @prop public var minPitch:Float = 0.5; + @prop public var enableJump:Bool = true; + @prop public var jumpForce:Float = 22.0; + @prop public var moveSpeed:Float = 500.0; - public function new() { - super(); + @prop public var forwardKey:String = "w"; + @prop public var backwardKey:String = "s"; + @prop public var leftKey:String = "a"; + @prop public var rightKey:String = "d"; + @prop public var jumpKey:String = "space"; - iron.Scene.active.notifyOnInit(init); - } + @prop public var allowAirJump:Bool = false; - function init() { - head = object.getChildOfType(CameraObject); + @prop public var canRun:Bool = true; + @prop public var runKey:String = "shift"; + @prop public var runSpeed:Float = 1000.0; - PhysicsWorld.active.notifyOnPreUpdate(preUpdate); - notifyOnUpdate(update); - notifyOnRemove(removed); - } + // Sistema de estamina + @prop public var stamina:Bool = false; + @prop public var staminaBase:Float = 75.0; + @prop public var staRecoverPerSec:Float = 5.0; + @prop public var staDecreasePerSec:Float = 5.0; + @prop public var staRecoverTime:Float = 2.0; + @prop public var staDecreasePerJump:Float = 5.0; + @prop public var enableFatigue:Bool = false; + @prop public var fatigueSpeed:Float = 0.5; // the reduction of movement when fatigue is activated... + @prop public var fatigueThreshold:Float = 30.0; // Tiempo corriendo sin parar para la activacion // Time running non-stop for activation... + @prop public var fatRecoveryThreshold:Float = 7.5; // Tiempo sin correr/saltar para salir de fatiga // Time without running/jumping to get rid of fatigue... + - var xVec = Vec4.xAxis(); - var zVec = Vec4.zAxis(); - function preUpdate() { - if (Input.occupied || !body.ready) return; + // Var Privadas + var head:CameraObject; + var pitch:Float = 0.0; + var body:RigidBody; - var mouse = Input.getMouse(); - var kb = Input.getKeyboard(); + var moveForward:Bool = false; + var moveBackward:Bool = false; + var moveLeft:Bool = false; + var moveRight:Bool = false; + var isRunning:Bool = false; - if (mouse.started() && !mouse.locked) mouse.lock(); - else if (kb.started("escape") && mouse.locked) mouse.unlock(); + var canJump:Bool = true; + var staminaValue:Float = 0.0; + var timeSinceStop:Float = 0.0; - if (mouse.locked || mouse.down()) { - head.transform.rotate(xVec, -mouse.movementY / 250 * rotationSpeed); - transform.rotate(zVec, -mouse.movementX / 250 * rotationSpeed); - body.syncTransform(); + var fatigueTimer:Float = 0.0; + var fatigueCooldown:Float = 0.0; + var isFatigueActive:Bool = false; + + public function new() { + super(); + iron.Scene.active.notifyOnInit(init); + } + + function init() { + body = object.getTrait(RigidBody); + head = object.getChildOfType(CameraObject); + PhysicsWorld.active.notifyOnPreUpdate(preUpdate); + notifyOnUpdate(update); + notifyOnRemove(removed); + staminaValue = staminaBase; + } + + function removed() { + PhysicsWorld.active.removePreUpdate(preUpdate); + } + + var zVec = Vec4.zAxis(); + + function preUpdate() { + if (Input.occupied || body == null) return; + var mouse = Input.getMouse(); + var kb = Input.getKeyboard(); + + if (mouse.started() && !mouse.locked) + mouse.lock(); + else if (kb.started("escape") && mouse.locked) + mouse.unlock(); + + if (mouse.locked || mouse.down()) { + var deltaTime:Float = iron.system.Time.delta; + object.transform.rotate(zVec, -mouse.movementX * rotationSpeed * deltaTime); + var deltaPitch:Float = -(mouse.movementY * rotationSpeed * deltaTime); + pitch += deltaPitch; + pitch = Math.max(minPitch, Math.min(maxPitch, pitch)); + head.transform.setRotation(pitch, 0.0, 0.0); + body.syncTransform(); + } + } + + var dir:Vec4 = new Vec4(); + + function isFatigued():Bool { + return enableFatigue && isFatigueActive; + } + + function update() { + if (body == null) return; + var deltaTime:Float = iron.system.Time.delta; + var kb = Input.getKeyboard(); + + moveForward = kb.down(forwardKey); + moveBackward = kb.down(backwardKey); + moveLeft = kb.down(leftKey); + moveRight = kb.down(rightKey); + var isMoving = moveForward || moveBackward || moveLeft || moveRight; + + var isGrounded:Bool = false; + #if lnx_physics + var vel = body.getLinearVelocity(); + if (Math.abs(vel.z) < 0.1) { + isGrounded = true; + } + #end + + // Dejo establecido el salto para tener en cuenta la (enableFatigue) si es que es false/true.... + if (isGrounded && !isFatigued()) { + canJump = true; } - } + // Saltar con estamina + if (enableJump && kb.started(jumpKey) && canJump) { + var jumpPower = jumpForce; + // Disminuir el salto al 50% si la (stamina) esta por debajo o en el 20%. + if (stamina) { + if (staminaValue <= 0) { + jumpPower = 0; + } else if (staminaValue <= staminaBase * 0.2) { + jumpPower *= 0.5; + } - function removed() { - PhysicsWorld.active.removePreUpdate(preUpdate); - } + staminaValue -= staDecreasePerJump; + if (staminaValue < 0.0) staminaValue = 0.0; + timeSinceStop = 0.0; + } - var dir = new Vec4(); - function update() { - if (!body.ready) return; + if (jumpPower > 0) { + body.applyImpulse(new Vec4(0, 0, jumpPower)); + if (!allowAirJump) canJump = false; + } + } - if (jump) { - body.applyImpulse(new Vec4(0, 0, 16)); - jump = false; + // Control de estamina y correr + if (canRun && kb.down(runKey) && isMoving) { + if (stamina) { + if (staminaValue > 0.0) { + isRunning = true; + staminaValue -= staDecreasePerSec * deltaTime; + if (staminaValue < 0.0) staminaValue = 0.0; + } else { + isRunning = false; + } + } else { + isRunning = true; + } + } else { + isRunning = false; + } + + // (temporizadores aparte) + if (isRunning) { + timeSinceStop = 0.0; + fatigueTimer += deltaTime; + fatigueCooldown = 0.0; + } else { + timeSinceStop += deltaTime; + fatigueCooldown += deltaTime; + } + + // Evitar correr y saltar al estar fatigado... + if (isFatigued()) { + isRunning = false; + canJump = false; } - // Move - dir.set(0, 0, 0); - if (moveForward) dir.add(transform.look()); - if (moveBackward) dir.add(transform.look().mult(-1)); - if (moveLeft) dir.add(transform.right().mult(-1)); - if (moveRight) dir.add(transform.right()); + // Activar fatiga despues de correr continuamente durante cierto umbral + if (enableFatigue && fatigueTimer >= fatigueThreshold) { + isFatigueActive = true; + } - // Push down - var btvec = body.getLinearVelocity(); - body.setLinearVelocity(0.0, 0.0, btvec.z - 1.0); + // Eliminar la fatiga despues de recuperarse + if (enableFatigue && isFatigueActive && fatigueCooldown >= fatRecoveryThreshold) { + isFatigueActive = false; + fatigueTimer = 0.0; + } - if (moveForward || moveBackward || moveLeft || moveRight) { - var dirN = dir.normalize(); - dirN.mult(6); - body.activate(); - body.setLinearVelocity(dirN.x, dirN.y, btvec.z - 1.0); - } + // Recuperar estamina si no esta corriendo + if (stamina && !isRunning && staminaValue < staminaBase && !isFatigued()) { + if (timeSinceStop >= staRecoverTime) { + staminaValue += staRecoverPerSec * deltaTime; + if (staminaValue > staminaBase) staminaValue = staminaBase; + } + } - // Keep vertical - body.setAngularFactor(0, 0, 0); - camera.buildMatrix(); - } -#end + // Movimiento ejes (local) + dir.set(0, 0, 0); + if (moveForward) dir.add(object.transform.look()); + if (moveBackward) dir.add(object.transform.look().mult(-1)); + if (moveLeft) dir.add(object.transform.right().mult(-1)); + if (moveRight) dir.add(object.transform.right()); + + var btvec = body.getLinearVelocity(); + body.setLinearVelocity(0.0, 0.0, btvec.z - 1.0); + + if (isMoving) { + var dirN = dir.normalize(); + var baseSpeed = moveSpeed; + if (isRunning && moveForward) { + baseSpeed = runSpeed; + } + var currentSpeed = isFatigued() ? baseSpeed * fatigueSpeed : baseSpeed; + dirN.mult(currentSpeed * deltaTime); + body.activate(); + body.setLinearVelocity(dirN.x, dirN.y, btvec.z - 1.0); + } + + body.setAngularFactor(0, 0, 0); + head.buildMatrix(); + } + + #end } + + +// Stamina and fatigue system..... \ No newline at end of file