Torque3D/Templates/Full/game/scripts/server/player.cs
2012-09-19 11:54:25 -04:00

509 lines
15 KiB
C#

//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
// Timeouts for corpse deletion.
$CorpseTimeoutValue = 45 * 1000;
// // Damage Rate for entering Liquid
// $DamageLava = 0.01;
// $DamageHotLava = 0.01;
// $DamageCrustyLava = 0.01;
// Death Animations
$PlayerDeathAnim::TorsoFrontFallForward = 1;
$PlayerDeathAnim::TorsoFrontFallBack = 2;
$PlayerDeathAnim::TorsoBackFallForward = 3;
$PlayerDeathAnim::TorsoLeftSpinDeath = 4;
$PlayerDeathAnim::TorsoRightSpinDeath = 5;
$PlayerDeathAnim::LegsLeftGimp = 6;
$PlayerDeathAnim::LegsRightGimp = 7;
$PlayerDeathAnim::TorsoBackFallForward = 8;
$PlayerDeathAnim::HeadFrontDirect = 9;
$PlayerDeathAnim::HeadBackFallForward = 10;
$PlayerDeathAnim::ExplosionBlowBack = 11;
//----------------------------------------------------------------------------
// Armor Datablock methods
//----------------------------------------------------------------------------
function Armor::onAdd(%this, %obj)
{
// Vehicle timeout
%obj.mountVehicle = true;
// Default dynamic armor stats
%obj.setRechargeRate(%this.rechargeRate);
%obj.setRepairRate(0);
// Set the numerical Health HUD
//%obj.updateHealth();
// Calling updateHealth() must be delayed now... for some reason
%obj.schedule(50, "updateHealth");
}
function Armor::onRemove(%this, %obj)
{
if (%obj.client.player == %obj)
%obj.client.player = 0;
}
function Armor::onNewDataBlock(%this, %obj)
{
}
//----------------------------------------------------------------------------
function Armor::onMount(%this, %obj, %vehicle, %node)
{
// Node 0 is the pilot's position, we need to dismount his weapon.
if (%node == 0)
{
%obj.setTransform("0 0 0 0 0 1 0");
%obj.setActionThread(%vehicle.getDatablock().mountPose[%node], true, true);
%obj.lastWeapon = %obj.getMountedImage($WeaponSlot);
%obj.unmountImage($WeaponSlot);
%obj.setControlObject(%vehicle);
if(%obj.getClassName() $= "Player")
commandToClient(%obj.client, 'toggleVehicleMap', true);
}
else
{
if (%vehicle.getDataBlock().mountPose[%node] !$= "")
%obj.setActionThread(%vehicle.getDatablock().mountPose[%node]);
else
%obj.setActionThread("root", true);
}
}
function Armor::onUnmount(%this, %obj, %vehicle, %node)
{
if (%node == 0)
{
%obj.mountImage(%obj.lastWeapon, $WeaponSlot);
%obj.setControlObject("");
}
}
function Armor::doDismount(%this, %obj, %forced)
{
//echo("\c4Armor::doDismount(" @ %this @", "@ %obj.client.nameBase @", "@ %forced @")");
// This function is called by player.cc when the jump trigger
// is true while mounted
%vehicle = %obj.mVehicle;
if (!%obj.isMounted() || !isObject(%vehicle))
return;
// Vehicle must be at rest!
if ((VectorLen(%vehicle.getVelocity()) <= %vehicle.getDataBlock().maxDismountSpeed ) || %forced)
{
// Position above dismount point
%pos = getWords(%obj.getTransform(), 0, 2);
%rot = getWords(%obj.getTransform(), 3, 6);
%oldPos = %pos;
%vec[0] = " -1 0 0";
%vec[1] = " 0 0 1";
%vec[2] = " 0 0 -1";
%vec[3] = " 1 0 0";
%vec[4] = "0 -1 0";
%impulseVec = "0 0 0";
%vec[0] = MatrixMulVector(%obj.getTransform(), %vec[0]);
// Make sure the point is valid
%pos = "0 0 0";
%numAttempts = 5;
%success = -1;
for (%i = 0; %i < %numAttempts; %i++)
{
%pos = VectorAdd(%oldPos, VectorScale(%vec[%i], 3));
if (%obj.checkDismountPoint(%oldPos, %pos))
{
%success = %i;
%impulseVec = %vec[%i];
break;
}
}
if (%forced && %success == -1)
%pos = %oldPos;
%obj.mountVehicle = false;
%obj.schedule(4000, "mountVehicles", true);
// Position above dismount point
%obj.unmount();
%obj.setTransform(%pos SPC %rot);//%obj.setTransform(%pos);
//%obj.playAudio(0, UnmountVehicleSound);
%obj.applyImpulse(%pos, VectorScale(%impulseVec, %obj.getDataBlock().mass));
// Set player velocity when ejecting
%vel = %obj.getVelocity();
%vec = vectorDot( %vel, vectorNormalize(%vel));
if(%vec > 50)
{
%scale = 50 / %vec;
%obj.setVelocity(VectorScale(%vel, %scale));
}
//%obj.vehicleTurret = "";
}
else
messageClient(%obj.client, 'msgUnmount', '\c2Cannot exit %1 while moving.', %vehicle.getDataBlock().nameTag);
}
//----------------------------------------------------------------------------
function Armor::onCollision(%this, %obj, %col)
{
if (!isObject(%col) || %obj.getState() $= "Dead")
return;
// Try and pickup all items
if (%col.getClassName() $= "Item")
{
%obj.pickup(%col);
return;
}
// Mount vehicles
if (%col.getType() & $TypeMasks::GameBaseObjectType)
{
%db = %col.getDataBlock();
if ((%db.getClassName() $= "WheeledVehicleData" ) && %obj.mountVehicle && %obj.getState() $= "Move" && %col.mountable)
{
// Only mount drivers for now.
ServerConnection.setFirstPerson(0);
// For this specific example, only one person can fit
// into a vehicle
%mount = %col.getMountNodeObject(0);
if(%mount)
return;
// For this specific FPS Example, always mount the player
// to node 0
%node = 0;
%col.mountObject(%obj, %node);
%obj.mVehicle = %col;
}
}
}
function Armor::onImpact(%this, %obj, %collidedObject, %vec, %vecLen)
{
%obj.damage(0, VectorAdd(%obj.getPosition(), %vec), %vecLen * %this.speedDamageScale, "Impact");
}
//----------------------------------------------------------------------------
function Armor::damage(%this, %obj, %sourceObject, %position, %damage, %damageType)
{
if (!isObject(%obj) || %obj.getState() $= "Dead" || !%damage)
return;
%obj.applyDamage(%damage);
%location = "Body";
// Update the numerical Health HUD
%obj.updateHealth();
// Deal with client callbacks here because we don't have this
// information in the onDamage or onDisable methods
%client = %obj.client;
%sourceClient = %sourceObject ? %sourceObject.client : 0;
if (isObject(%client))
{
// Determine damage direction
if (%damageType !$= "Suicide")
%obj.setDamageDirection(%sourceObject, %position);
if (%obj.getState() $= "Dead")
%client.onDeath(%sourceObject, %sourceClient, %damageType, %location);
}
}
function Armor::onDamage(%this, %obj, %delta)
{
// This method is invoked by the ShapeBase code whenever the
// object's damage level changes.
if (%delta > 0 && %obj.getState() !$= "Dead")
{
// Apply a damage flash
%obj.setDamageFlash(1);
// If the pain is excessive, let's hear about it.
if (%delta > 10)
%obj.playPain();
}
}
// ----------------------------------------------------------------------------
// The player object sets the "disabled" state when damage exceeds it's
// maxDamage value. This is method is invoked by ShapeBase state mangement code.
// If we want to deal with the damage information that actually caused this
// death, then we would have to move this code into the script "damage" method.
function Armor::onDisabled(%this, %obj, %state)
{
// Release the main weapon trigger
%obj.setImageTrigger(0, false);
// Toss current mounted weapon and ammo if any
%item = %obj.getMountedImage($WeaponSlot).item;
if (isObject(%item))
{
%amount = %obj.getInventory(%item.image.ammo);
if (!%item.image.clip)
warn("No clip exists to throw for item ", %item);
if(%amount)
%obj.throw(%item.image.clip, 1);
}
// Toss out a health patch
%obj.tossPatch();
%obj.playDeathCry();
%obj.playDeathAnimation();
//%obj.setDamageFlash(0.75);
// Disable any vehicle map
commandToClient(%obj.client, 'toggleVehicleMap', false);
// Schedule corpse removal. Just keeping the place clean.
%obj.schedule($CorpseTimeoutValue - 1000, "startFade", 1000, 0, true);
%obj.schedule($CorpseTimeoutValue, "delete");
}
//-----------------------------------------------------------------------------
function Armor::onLeaveMissionArea(%this, %obj)
{
//echo("\c4Leaving Mission Area at POS:"@ %obj.getPosition());
// Inform the client
%obj.client.onLeaveMissionArea();
// Damage over time and kill the coward!
//%obj.setDamageDt(0.2, "MissionAreaDamage");
}
function Armor::onEnterMissionArea(%this, %obj)
{
//echo("\c4Entering Mission Area at POS:"@ %obj.getPosition());
// Inform the client
%obj.client.onEnterMissionArea();
// Stop the punishment
//%obj.clearDamageDt();
}
//-----------------------------------------------------------------------------
function Armor::onEnterLiquid(%this, %obj, %coverage, %type)
{
//echo("\c4this:"@ %this @" object:"@ %obj @" just entered water of type:"@ %type @" for "@ %coverage @"coverage");
}
function Armor::onLeaveLiquid(%this, %obj, %type)
{
//
}
//-----------------------------------------------------------------------------
function Armor::onTrigger(%this, %obj, %triggerNum, %val)
{
// This method is invoked when the player receives a trigger move event.
// The player automatically triggers slot 0 and slot one off of triggers #
// 0 & 1. Trigger # 2 is also used as the jump key.
}
//-----------------------------------------------------------------------------
function Armor::onPoseChange(%this, %obj, %oldPose, %newPose)
{
// Set the script anim prefix to be that of the current pose
%obj.setImageScriptAnimPrefix( $WeaponSlot, addTaggedString(%newPose) );
}
//-----------------------------------------------------------------------------
function Armor::onStartSprintMotion(%this, %obj)
{
%obj.setImageGenericTrigger($WeaponSlot, 0, true);
}
function Armor::onStopSprintMotion(%this, %obj)
{
%obj.setImageGenericTrigger($WeaponSlot, 0, false);
}
//-----------------------------------------------------------------------------
// Player methods
//-----------------------------------------------------------------------------
//----------------------------------------------------------------------------
function Player::kill(%this, %damageType)
{
%this.damage(0, %this.getPosition(), 10000, %damageType);
}
//----------------------------------------------------------------------------
function Player::mountVehicles(%this, %bool)
{
// If set to false, this variable disables vehicle mounting.
%this.mountVehicle = %bool;
}
function Player::isPilot(%this)
{
%vehicle = %this.getObjectMount();
// There are two "if" statements to avoid a script warning.
if (%vehicle)
if (%vehicle.getMountNodeObject(0) == %this)
return true;
return false;
}
//----------------------------------------------------------------------------
function Player::playDeathAnimation(%this)
{
%numDeathAnimations = %this.getNumDeathAnimations();
if ( %numDeathAnimations > 0 )
{
if (isObject(%this.client))
{
if (%this.client.deathIdx++ > %numDeathAnimations)
%this.client.deathIdx = 1;
%this.setActionThread("Death" @ %this.client.deathIdx);
}
else
{
%rand = getRandom(1, %numDeathAnimations);
%this.setActionThread("Death" @ %rand);
}
}
}
function Player::playCelAnimation(%this, %anim)
{
if (%this.getState() !$= "Dead")
%this.setActionThread("cel"@%anim);
}
//----------------------------------------------------------------------------
function Player::playDeathCry(%this)
{
%this.playAudio(0, DeathCrySound);
}
function Player::playPain(%this)
{
%this.playAudio(0, PainCrySound);
}
// ----------------------------------------------------------------------------
// Numerical Health Counter
// ----------------------------------------------------------------------------
function Player::updateHealth(%player)
{
//echo("\c4Player::updateHealth() -> Player Health changed, updating HUD!");
// Calcualte player health
%maxDamage = %player.getDatablock().maxDamage;
%damageLevel = %player.getDamageLevel();
%curHealth = %maxDamage - %damageLevel;
%curHealth = mceil(%curHealth);
// Send the player object's current health level to the client, where it
// will Update the numericalHealth HUD.
commandToClient(%player.client, 'setNumericalHealthHUD', %curHealth);
}
function Player::setDamageDirection(%player, %sourceObject, %damagePos)
{
if (isObject(%sourceObject))
{
if (%sourceObject.isField(initialPosition))
{
// Projectiles have this field set to the muzzle point of
// the firing weapon at the time the projectile was created.
// This gives a damage direction towards the firing player,
// turret, vehicle, etc. Bullets and weapon fired grenades
// are examples of projectiles.
%damagePos = %sourceObject.initialPosition;
}
else
{
// Other objects that cause damage, such as mines, use their own
// location as the damage position. This gives a damage direction
// towards the explosive origin rather than the person that lay the
// explosives.
%damagePos = %sourceObject.getPosition();
}
}
// Rotate damage vector into object space
%damageVec = VectorSub(%damagePos, %player.getWorldBoxCenter());
%damageVec = VectorNormalize(%damageVec);
%damageVec = MatrixMulVector(%player.client.getCameraObject().getInverseTransform(), %damageVec);
// Determine largest component of damage vector to get direction
%vecComponents = -%damageVec.x SPC %damageVec.x SPC -%damageVec.y SPC %damageVec.y SPC -%damageVec.z SPC %damageVec.z;
%vecDirections = "Left" SPC "Right" SPC "Bottom" SPC "Front" SPC "Bottom" SPC "Top";
%max = -1;
for (%i = 0; %i < 6; %i++)
{
%value = getWord(%vecComponents, %i);
if (%value > %max)
{
%max = %value;
%damageDir = getWord(%vecDirections, %i);
}
}
commandToClient(%player.client, 'setDamageDirection', %damageDir);
}
function Player::use(%player, %data)
{
// No mounting/using weapons when you're driving!
if (%player.isPilot())
return(false);
Parent::use(%player, %data);
}