In that case, I better post what I have for the grappler. Note that I couldn't test it on players yet.
Btw entering an invo should disengage the grappler, as you can now grapple onto something and switch to a different loadout with no hook.
Code:
//---------------------------------------------------------------------
// Grapple Hook
// 6/11/2004 - June 11, 2004
// Made by Lt Earthworm (looks/sounds) and Amadeu5 (physics)
// I actually made this on impulse. I watched a Tribes : Vengeance
// video with the grappling hook in it and though it was really awesome.
// I did not want to wait until the game was out to be able to use it,
// Thus, the Tribes 2 Grappling Hook is born.
//---------------------------------------------------------------------
$GrappleHook::MaxLength = 75; // Max length of grapple hook
$GrappleHook::UseOtherWeapons = 1; // Ability to use other weapons while grappled. 0 for no, 1 for yes
$GrappleHook::RopeStrength = 1.3; // How much can we stretch the rope before it breaks.
$GrappleHook::ScheduleTime = 35;
datablock AudioProfile(GrappleLineBreakSound)
{
filename = "fx/weapons/cg_metal2.wav";
description = AudioDefault3d;
preload = true;
effect = TargetingLaserPaintEffect;
};
datablock AudioProfile(GrappleFireSound)
{
filename = "fx/vehicles/bomber_bomb_dryfire.wav"; // shrike_blaster_projectile_impact.wav
description = AudioDefault3d;
preload = true;
effect = TargetingLaserPaintEffect;
};
datablock AudioProfile(GrappleMissSound)
{
filename = "fx/vehicles/crash_grav_soft.wav";
description = AudioDefault3d;
preload = true;
effect = TargetingLaserSwitchEffect;
};
datablock TargetProjectileData(GrappleBeam) : BasicTargeter
{
maxRifleRange = 100;
beamColor = "0.3 0.3 0.3";
pulseBeamWidth = 0;
beamFlareAngle = 0;
maxFlareSize = 0;
pulseSpeed = 0;
pulseLength = 0;
coupleBeam = 0;
beacon = false;
};
datablock ItemData(GrapplingHook) : ELFGun
{
shapeFile = "weapon_elf.dts";
image = GrapplingHookImage;
pickUpName = "a grappling hook";
};
datablock ShapeBaseImageData(GrapplingHookImage)
{
className = WeaponImage;
shapeFile = "weapon_elf.dts";
item = GrapplingHook;
projectile = GrappleBeam;
projectileType = TargetProjectile;
deleteLastProjectile = true;
usesEnergy = true;
fireEnergy = 1;
minEnergy = 1;
stateName[0] = "Activate";
stateTransitionOnTimeout[0] = "ActivateReady";
stateTimeoutValue[0] = 0.5;
stateSequence[0] = "Activate";
stateSound[0] = BlasterSwitchSound;
stateName[1] = "ActivateReady";
stateTransitionOnLoaded[1] = "Ready";
stateTransitionOnNoAmmo[1] = "NoAmmo";
stateName[2] = "Ready";
stateTransitionOnNoAmmo[2] = "NoAmmo";
stateTransitionOnTriggerDown[2] = "Fire";
stateName[3] = "Fire";
stateTransitionOnTimeout[3] = "Reload";
stateTimeoutValue[3] = 0.3;
stateFire[3] = true;
stateRecoil[3] = NoRecoil;
stateAllowImageChange[3] = false;
stateSequence[3] = "Fire";
//stateSound[3] = GrappleFireSound;
stateScript[3] = "onFire";
stateName[4] = "Reload";
stateTransitionOnNoAmmo[4] = "NoAmmo";
stateTransitionOnTimeout[4] = "Ready";
stateAllowImageChange[4] = false;
stateSequence[4] = "Reload";
stateName[5] = "NoAmmo";
stateTransitionOnAmmo[5] = "Reload";
stateSequence[5] = "NoAmmo";
stateTransitionOnTriggerDown[5] = "DryFire";
stateName[6] = "DryFire";
stateTimeoutValue[6] = 0.3;
stateSound[6] = BlasterDryFireSound;
stateTransitionOnTimeout[6] = "Ready";
};
// Functions
function GrapplingHookImage::onFire(%data, %player, %slot)
{
// If the player has already grappled and if so remove the grapple
if(%player.isHooked)
{
%player.disengageGrapple();
return;
}
// ZOD - lets do this a little differently
%p = Parent::onFire(%data, %player, %slot);
if(isObject(%p))
{
%pPos = getWord(%p.getTargetPoint(), 0) SPC
getWord(%p.getTargetPoint(), 1) SPC
getWord(%p.getTargetPoint(), 2);
}
// Get the muzzle position
%point = %player.getMuzzlePoint(%slot);
// Get the muzzle vector
%vector = %player.getMuzzleVector(%slot);
%vector = vectorScale(%vector, $GrappleHook::MaxLength);
// Get the end point for the Raycast
%endPoint = vectorAdd(%point, %vector);
// Stuff to look for
%mask = $TypeMasks::InteriorObjectType | $TypeMasks::StaticShapeObjectType |
$TypeMasks::PlayerObjectType | $TypeMasks::VehicleObjectType |
$TypeMasks::TerrainObjectType | $TypeMasks::ForceFieldObjectType |
$TypeMasks::StaticTSObjectType;
// Raycast to see if we hit anything
%search = containerRayCast(%point, %endPoint, %mask, %player);
// Nothing found. Can't do anything else
if(!%search)
{
serverPlay3D(GrappleMissSound, %player.getTransform());
if(isObject(%p))
%p.delete();
return;
}
// Play the latch on sound
serverPlay3D(GrappleFireSound, %player.getTransform());
// We start off with an intact rope
%player.LineIsBroken = false;
%hitObj = FirstWord(%search);
// Don't want to grapple onto those - ZOD, pfft
if( %hitObj.getType() & ($TypeMasks::TerrainObjectType | $TypeMasks::ForceFieldObjectType | $TypeMasks::InteriorObjectType) )
%RayPos = %pPos;
// Yay! We hit something we can grapple!
%player.isHooked = true;
// get the position from the raycast
if(%search)
%RayPos = posFromRaycast(%search);
// Get the vector lenght from %point to %RayPos
%player.grappleLength = vectorLen(vectorSub(%point, %RayPos));
if(isObject(%player.lastProjectile))
%player.lastProjectile.delete();
if(%hitObj && (%hitObj.getType() & ($TypeMasks::PlayerObjectType | $TypeMasks::VehicleObjectType)))
%player.moveGrappleSwingLoop(%hitObj); // They got a moving object!
else // Fixed grapple
%player.fixedGrappleSwingLoop(%RayPos);
}
function Player::fixedGrappleSwingLoop(%player, %SwingPos)
{
if(!isObject(%player))
return;
if(isEventPending(%player.grappleSchedule))
cancel(%player.grappleSchedule);
if(isObject(%player.rope))
%player.rope.delete();
if(%player.isHooked == false)
return;
if(%player.getState() !$= "Dead")
{
%vel = %player.getVelocity();
%pos = %player.getPosition();
%rad = VectorSub(%SwingPos, %pos);
%dist = VectorLen(%rad);
// Ugly vector math beyond this point, keep out if you don't know what you're doing
if (%dist > %player.grappleLength &&
VectorLen(VectorSub(%SwingPos, VectorAdd(%pos, %vel))) > %player.grappleLength) {
%force = VectorProject(%vel, %rad);
%surplus = %dist - %player.grappleLength;
%tanVel = VectorProject(%vel, VectorCross(%rad, VectorCross(%rad, %vel)));
%value = %tanVel+(getWord(%rad, 2)/%player.grappleLength)*%force;
// Break the line if there's too much stress on it
if (%value / %player.grappleLength > $GrappleHook::RopeStrength) {
%player.LineIsBroken = true;
%player.disengageGrapple();
}
else {
%mass = %player.getDataBlock().mass;
%vec = VectorScale(VectorNormalize(%rad), (%dist / %player.grappleLength) * mAbs(%force) * %mass / 2);
%player.ApplyImpulse(%player.getPosition(), %vec);
}
}
// Create the rope
%player.rope = new TargetProjectile() {
datablock = GrappleBeam;
sourceSlot = 0;
//sourceObject = %player;
initialPosition = %player.getMuzzlePoint(0);
initialDirection = %rad;
};
// Add it to the MissionCleanup Simgroup
MissionCleanup.add(%player.rope);
%player.grappleSchedule = %player.schedule($GrappleHook::ScheduleTime, "fixedGrappleSwingLoop", %SwingPos);
}
}
function Player::moveGrappleSwingLoop(%player, %followObj)
{
if(!isObject(%player))
return;
if(isEventPending(%player.grappleSchedule))
cancel(%player.grappleSchedule);
if(isObject(%player.rope))
%player.rope.delete();
// Stop if they have un-grappled
if(%player.isHooked == false)
return;
if(%player.getState() !$= "Dead")
{
// Can't quite grapple to a non-existent object, can you?
if(!isObject(%followObj))
return;
%SwingPos = %followObj.getPosition();
%vel = %player.getVelocity();
%pos = %player.getPosition();
%rad = VectorSub(%SwingPos, %pos);
%dist = VectorLen(%rad);
// Ugly vector math beyond this point, keep out if you don't know what you're doing
if (%dist > %player.grappleLength &&
VectorLen(VectorSub(%SwingPos, VectorAdd(%pos, %vel))) > %player.grappleLength) {
%force = VectorProject(%vel, %rad);
%surplus = %dist - %player.grappleLength;
%tanVel = VectorProject(%vel, VectorCross(%rad, VectorCross(%rad, %vel)));
%value = %tanVel+(getWord(%rad, 2)/%player.grappleLength)*%force;
//Break the line if there's too much stress on it
if (%value / %player.grappleLength > $GrappleHook::RopeStrength) {
%player.LineIsBroken = true;
%player.disengageGrapple();
}
else {
%mass = %player.getDataBlock().mass;
%vec = VectorScale(VectorNormalize(%rad), (%dist / %player.grappleLength) * mAbs(%force) * %mass / 2);
%player.ApplyImpulse(%player.getPosition(), %vec);
// Let's not leave the poor bastard alone
%followObj.applyImpulse(%followObj.getPosition, VectorScale(%vec, 1/%followObj.getDatablock().mass));
}
}
// Create the rope
%player.rope = new TargetProjectile() {
datablock = GrappleBeam;
sourceSlot = 0;
//sourceObject = %player;
initialPosition = %player.getMuzzlePoint(0);
initialDirection = %vec;
};
// Add it to the MissionCleanup Simgroup
MissionCleanup.add(%player.rope);
%player.grappleSchedule = %player.schedule($GrappleHook::ScheduleTime, "moveGrappleSwingLoop", %followObj);
}
}
function Player::disengageGrapple(%player)
{
// Un-Grapple the player
%player.isHooked = false;
// If we were clumsy and broke the line then play a sound
if (%player.LineIsBroken) serverPlay3D(GrappleLineBreakSound, %player.getTransform());
if(isObject(%player.rope))
%player.rope.delete();
if(isEventPending(%player.grappleSchedule))
cancel(%player.grappleSchedule);
}
function GrappleHookImage::onUnmount(%data, %player, %slot)
{
if(!$GrappleHook::UseOtherWeapons)
DisengageGrapple(%player);
}
function VectorProject(%vec, %target) {
return ((getWord(%vec, 0)*getWord(%target, 0))
+ (getWord(%vec, 1)*getWord(%target, 1))
+ (getWord(%vec, 2)*getWord(%target, 2))) / VectorLen(%target);
}
Btw entering an invo should disengage the grappler, as you can now grapple onto something and switch to a different loadout with no hook.