#include "Scripts/NinjaSnowWar/GameObject.as" #include "Scripts/NinjaSnowWar/AIController.as" const int LAYER_MOVE = 0; const int LAYER_ATTACK = 1; const float ninjaMoveForce = 25; const float ninjaAirMoveForce = 1; const float ninjaDampingForce = 5; const float ninjaJumpForce = 450; const Vector3 ninjaThrowVelocity(0, 4.25, 20); const Vector3 ninjaThrowPosition(0, 0.2, 1); const float ninjaThrowDelay = 0.1; const float ninjaCorpseDuration = 3; const int ninjaPoints = 250; class Ninja : GameObject { Controls controls; Controls prevControls; AIController@ controller; bool okToJump; bool smoke; float inAirTime; float onGroundTime; float throwTime; float deathTime; float deathDir; float dirChangeTime; float aimX; float aimY; Ninja() { health = maxHealth = 2; okToJump = false; smoke = false; onGround = false; isSliding = false; inAirTime = 1; onGroundTime = 0; throwTime = 0; deathTime = 0; deathDir = 0; dirChangeTime = 0; aimX = 0; aimY = 0; } void Start() { SubscribeToEvent(node, "NodeCollision", "HandleNodeCollision"); aimX = node.rotation.yaw; } void DelayedStart() { // Start playing the idle animation immediately, even before the first physics update AnimationController@ animCtrl = node.children[0].GetComponent("AnimationController"); animCtrl.PlayExclusive("Models/NinjaSnowWar/Ninja_Idle3.ani", LAYER_MOVE, true); } void SetControls(const Controls&in newControls) { controls = newControls; } Quaternion GetAim() { Quaternion q = Quaternion(aimX, Vector3(0, 1, 0)); q = q * Quaternion(aimY, Vector3(1, 0, 0)); return q; } void FixedUpdate(float timeStep) { // For multiplayer, replicate the health into the node user variables node.vars["Health"] = health; if (health <= 0) { DeathUpdate(timeStep); return; } // AI control if controller exists if (controller !is null) controller.Control(this, node, timeStep); RigidBody@ body = node.GetComponent("RigidBody"); AnimationController@ animCtrl = node.children[0].GetComponent("AnimationController"); // Turning / horizontal aiming if (aimX != controls.yaw) aimX = controls.yaw; // Vertical aiming if (aimY != controls.pitch) aimY = controls.pitch; // Force the physics rotation Quaternion q(aimX, Vector3(0, 1, 0)); body.rotation = q; // Movement ground/air Vector3 vel = body.linearVelocity; if (onGround) { // If landed, play a particle effect at feet (use the AnimatedModel node) if (inAirTime > 0.5) SpawnParticleEffect(node.children[0].worldPosition, "Particle/SnowExplosion.xml", 1); inAirTime = 0; onGroundTime += timeStep; } else { onGroundTime = 0; inAirTime += timeStep; } if (inAirTime < 0.3f && !isSliding) { bool sideMove = false; // Movement in four directions if (controls.IsDown(CTRL_UP|CTRL_DOWN|CTRL_LEFT|CTRL_RIGHT)) { float animDir = 1.0f; Vector3 force(0, 0, 0); if (controls.IsDown(CTRL_UP)) force += q * Vector3(0, 0, 1); if (controls.IsDown(CTRL_DOWN)) { animDir = -1.0f; force += q * Vector3(0, 0, -1); } if (controls.IsDown(CTRL_LEFT)) { sideMove = true; force += q * Vector3(-1, 0, 0); } if (controls.IsDown(CTRL_RIGHT)) { sideMove = true; force += q * Vector3(1, 0, 0); } // Normalize so that diagonal strafing isn't faster force.Normalize(); force *= ninjaMoveForce; body.ApplyImpulse(force); // Walk or sidestep animation if (sideMove) { animCtrl.PlayExclusive("Models/NinjaSnowWar/Ninja_Stealth.ani", LAYER_MOVE, true, 0.2); animCtrl.SetSpeed("Models/NinjaSnowWar/Ninja_Stealth.ani", animDir * 2.2); } else { animCtrl.PlayExclusive("Models/NinjaSnowWar/Ninja_Walk.ani", LAYER_MOVE, true, 0.2); animCtrl.SetSpeed("Models/NinjaSnowWar/Ninja_Walk.ani", animDir * 1.6); } } else { // Idle animation animCtrl.PlayExclusive("Models/NinjaSnowWar/Ninja_Idle3.ani", LAYER_MOVE, true, 0.2); } // Overall damping to cap maximum speed body.ApplyImpulse(Vector3(-ninjaDampingForce * vel.x, 0, -ninjaDampingForce * vel.z)); // Jumping if (controls.IsDown(CTRL_JUMP)) { if (okToJump && inAirTime < 0.1f) { // Lift slightly off the ground for better animation body.position = body.position + Vector3(0, 0.03, 0); body.ApplyImpulse(Vector3(0, ninjaJumpForce, 0)); inAirTime = 1.0f; animCtrl.PlayExclusive("Models/NinjaSnowWar/Ninja_JumpNoHeight.ani", LAYER_MOVE, false, 0.1); animCtrl.SetTime("Models/NinjaSnowWar/Ninja_JumpNoHeight.ani", 0.0); // Always play from beginning okToJump = false; } } else okToJump = true; } else { // Motion in the air // Note: when sliding a steep slope, control (or damping) isn't allowed! if (inAirTime > 0.3f && !isSliding) { if (controls.IsDown(CTRL_UP|CTRL_DOWN|CTRL_LEFT|CTRL_RIGHT)) { Vector3 force(0, 0, 0); if (controls.IsDown(CTRL_UP)) force += q * Vector3(0, 0, 1); if (controls.IsDown(CTRL_DOWN)) force += q * Vector3(0, 0, -1); if (controls.IsDown(CTRL_LEFT)) force += q * Vector3(-1, 0, 0); if (controls.IsDown(CTRL_RIGHT)) force += q * Vector3(1, 0, 0); // Normalize so that diagonal strafing isn't faster force.Normalize(); force *= ninjaAirMoveForce; body.ApplyImpulse(force); } } // Falling/jumping/sliding animation if (inAirTime > 0.1f) animCtrl.PlayExclusive("Models/NinjaSnowWar/Ninja_JumpNoHeight.ani", LAYER_MOVE, false, 0.1); } // Shooting if (throwTime >= 0) throwTime -= timeStep; // Start fading the attack animation after it has progressed past a certain point if (animCtrl.GetTime("Models/NinjaSnowWar/Ninja_Attack1.ani") > 0.1) animCtrl.Fade("Models/NinjaSnowWar/Ninja_Attack1.ani", 0.0, 0.5); if ((controls.IsPressed(CTRL_FIRE, prevControls)) && (throwTime <= 0)) { Vector3 projectileVel = GetAim() * ninjaThrowVelocity; animCtrl.Play("Models/NinjaSnowWar/Ninja_Attack1.ani", LAYER_ATTACK, false, 0.0); animCtrl.SetTime("Models/NinjaSnowWar/Ninja_Attack1.ani", 0.0); // Always play from beginning Node@ snowball = SpawnObject(node.position + vel * timeStep + q * ninjaThrowPosition, GetAim(), "SnowBall"); RigidBody@ snowballBody = snowball.GetComponent("RigidBody"); snowballBody.linearVelocity = projectileVel; GameObject@ snowballObject = cast(snowball.scriptObject); snowballObject.side = side; snowballObject.creatorID = node.id; PlaySound("Sounds/NutThrow.wav"); throwTime = ninjaThrowDelay; } prevControls = controls; ResetWorldCollision(); } void DeathUpdate(float timeStep) { RigidBody@ body = node.GetComponent("RigidBody"); CollisionShape@ shape = node.GetComponent("CollisionShape"); Node@ modelNode = node.children[0]; AnimationController@ animCtrl = modelNode.GetComponent("AnimationController"); AnimatedModel@ model = modelNode.GetComponent("AnimatedModel"); Vector3 vel = body.linearVelocity; // Overall damping to cap maximum speed body.ApplyImpulse(Vector3(-ninjaDampingForce * vel.x, 0, -ninjaDampingForce * vel.z)); // Collide only to world geometry body.collisionMask = 2; // Pick death animation on first death update if (deathDir == 0) { if (Random(1.0) < 0.5) deathDir = -1; else deathDir = 1; PlaySound("Sounds/SmallExplosion.wav"); VariantMap eventData; eventData["Points"] = ninjaPoints; eventData["Receiver"] = lastDamageCreatorID; eventData["DamageSide"] = lastDamageSide; SendEvent("Points", eventData); SendEvent("Kill", eventData); } deathTime += timeStep; // Move the model node to center the corpse mostly within the physics cylinder // (because of the animation) if (deathDir < 0) { // Backward death animCtrl.StopLayer(LAYER_ATTACK, 0.1); animCtrl.PlayExclusive("Models/NinjaSnowWar/Ninja_Death1.ani", LAYER_MOVE, false, 0.2); animCtrl.SetSpeed("Models/NinjaSnowWar/Ninja_Death1.ani", 0.5); if ((deathTime >= 0.3) && (deathTime < 0.8)) modelNode.Translate(Vector3(0, 0, 4.25 * timeStep)); } else if (deathDir > 0) { // Forward death animCtrl.StopLayer(LAYER_ATTACK, 0.1); animCtrl.PlayExclusive("Models/NinjaSnowWar/Ninja_Death2.ani", LAYER_MOVE, false, 0.2); animCtrl.SetSpeed("Models/NinjaSnowWar/Ninja_Death2.ani", 0.5); if ((deathTime >= 0.4) && (deathTime < 0.8)) modelNode.Translate(Vector3(0, 0, -4.25 * timeStep)); } // Create smokecloud just before vanishing if ((deathTime > (ninjaCorpseDuration - 1)) && (!smoke)) { SpawnParticleEffect(node.position + Vector3(0, -0.4, 0), "Particle/Smoke.xml", 8); smoke = true; } if (deathTime > ninjaCorpseDuration) { SpawnObject(node.position + Vector3(0, -0.5, 0), Quaternion(), "LightFlash"); SpawnSound(node.position + Vector3(0, -0.5, 0), "Sounds/BigExplosion.wav", 2); node.Remove(); } } bool Heal(int amount) { if (health == maxHealth) return false; health += amount; if (health > maxHealth) health = maxHealth; // If player, play the "powerup" sound if (side == SIDE_PLAYER) PlaySound("Sounds/Powerup.wav"); return true; } }