---------------------------------------------------------------------------------------------------------------
RepairGun / RepairRifle Tutorial
---------------------------------------------------------------------------------------------------------------
This tutorial will make two different repair guns. The first, is just like the repair pack, except it's a weapon. It refills health twice as fast as the pack, so is recommended for only Engineers/Mediums. The second gun, is a long range repair rifle. It fills energy at half the speed of the repair pack, and requires the energy pack, but it can shoot up to a 1000 meters.
-----------------------------------------------------------------------------
Engineer Repair Gun
Step #1
// ------------------------------------------
// inventoryHud.cs
// ------------------------------------------
$InvWeapon[12] = "Repair Gun";
$NameToInv["Repair Gun"] = "Repairgun2";
Step #2
// ------------------------------------------
// weapons.cs
// ------------------------------------------
$WeaponsHudData[12, bitmapName] = "gui/hud_new_packrepair";
$WeaponsHudData[12, itemDataName] = "Repairgun2";
$WeaponsHudCount = 13;
exec("scripts/weapons/Repairgun.cs");
Step #3
// ------------------------------------------
// inventory.cs
// ------------------------------------------
3a)
function ShapeBase::hasAmmo( %this, %weapon )
{
switch$ ( %weapon )
{
case RepairGun2:
return( true );
}
}
3b)
function ShapeBase::clearInventory(%this)
{
%this.setInventory(Repairgun2,0);
}
3c)
function serverCmdGiveAll(%client)
{
if($TestCheats)
{
%player.setInventory(Repairgun2,1);
}
}
Step #4
// ------------------------------------------
// player.cs
// ------------------------------------------
4a) Add these to the player datablocks. Recommend only Engineers/Mediums.
max [RepairGun2] = 1;
4b) Add these to function armor::applyConcussion
if( %weaps[12] = %player.getInventory("Repairgun2") > 0 ) %numWeapons++;
and
case 12:
%player.use("Repairgun2");
Step #5
// ------------------------------------------
// hud.cs
// ------------------------------------------
Skip this if you use any server side reticle fixes.
function clientCmdSetWeaponsHudActive(%slot)
{
weaponsHud.setActiveWeapon(%slot);
switch$($WeaponNames[%slot])
{
case "RepairGun2":
reticleHud.setBitmap("gui/ret_chaingun");
reticleFrameHud.setVisible(true);
}
}
Step #6
// ------------------------------------------
// OptionsDlg.cs
// ------------------------------------------
$RemapName[$RemapCount] = "Repairgun2";
$RemapCmd[$RemapCount] = "useRepairgun2";
$RemapCount++;
Step #7
// ------------------------------------------
// ControlDefaults.cs
// ------------------------------------------
function useRepairgun2( %val )
{
if ( %val )
use( Repairgun2 );
}
Step #8
// ------------------------------------------
// RepairGun.cs
// ------------------------------------------
Make a file called RepairGun.cs in your weapons directory and add all this:
//--------------------------------------------------------------------------
//Repair Gun
//
//
//--------------------------------------------------------------------------
datablock ItemData(RepairGun2)
{
className = Weapon;
catagory = "Spawn Items";
shapeFile = "weapon_repair.dts";
image = RepairGunImage2;
mass = 1;
elasticity = 0.2;
friction = 0.6;
pickupRadius = 2;
pickUpName = "a repair gun";
computeCRC = true;
emap = true;
};
//--------------------------------------------------------------------------
// Repair Gun
datablock ShapeBaseImageData(RepairGunImage2)
{
className = WeaponImage;
shapeFile = "weapon_repair.dts";
offset = "0 0 0";
item = Repairgun2;
showname = "Repair Gun";
usesEnergy = true;
minEnergy = 3;
cutOffEnergy = 3.1;
emap = true;
repairFactorPlayer = 0.004; // <--- Double RepairPack
repairFactorObject = 0.008; // <--- Double RepairPack
stateName[0] = "Activate";
stateTransitionOnTimeout[0] = "ActivateReady";
stateTimeoutValue[0] = 0.25;
stateName[1] = "ActivateReady";
stateScript[1] = "onActivateReady";
stateSpinThread[1] = Stop;
stateTransitionOnAmmo[1] = "Ready";
stateTransitionOnNoAmmo[1] = "ActivateReady";
stateName[2] = "Ready";
stateSpinThread[2] = Stop;
stateTransitionOnNoAmmo[2] = "Deactivate";
stateTransitionOnTriggerDown[2] = "Validate";
stateName[3] = "Validate";
stateTransitionOnTimeout[3] = "Validate";
stateTimeoutValue[3] = 0.2;
stateEnergyDrain[3] = 3;
stateSpinThread[3] = SpinUp;
stateScript[3] = "onValidate";
stateIgnoreLoadedForReady[3] = true;
stateTransitionOnLoaded[3] = "Repair";
stateTransitionOnNoAmmo[3] = "Deactivate";
stateTransitionOnTriggerUp[3] = "Deactivate";
stateName[4] = "Repair";
stateSound[4] = RepairPackFireSound;
stateScript[4] = "onRepair";
stateSpinThread[4] = FullSpeed;
stateAllowImageChange[4] = false;
stateSequence[4] = "activate";
stateFire[4] = true;
stateEnergyDrain[4] = 9;
stateTimeoutValue[4] = 0.2;
stateTransitionOnTimeOut[4] = "Repair";
stateTransitionOnNoAmmo[4] = "Deactivate";
stateTransitionOnTriggerUp[4] = "Deactivate";
stateTransitionOnNotLoaded[4] = "Validate";
stateName[5] = "Deactivate";
stateScript[5] = "onDeactivate";
stateSpinThread[5] = SpinDown;
stateSequence[5] = "activate";
stateDirection[5] = false;
stateTimeoutValue[5] = 0.2;
stateTransitionOnTimeout[5] = "ActivateReady";
};
function RepairGunImage2::onUnmount(%this,%obj,%slot)
{
// called when player switches to another weapon
// stop repairing whatever player was repairing
if(%obj.repairing)
stopRepairing(%obj);
%obj.setImageTrigger(%slot, false);
// "turn off" the repair pack -- player needs to hit the "pack" key to
// activate the repair gun again
}
function RepairGunImage2::onActivateReady(%this,%obj,%slot)
{
%obj.errMsgSent = false;
%obj.selfRepairing = false;
%obj.repairing = 0;
%obj.setImageLoaded(%slot, false);
}
function RepairGunImage2::onValidate(%this,%obj,%slot)
{
// this = repairgunimage datablock
// obj = player wielding the repair gun
// slot = weapon slot
if(%obj.getEnergyLevel() <= %this.cutOffEnergy)
{
stopRepairing(%obj);
return;
}
%repGun = %obj.getMountedImage(%slot);
// muzVec is the vector coming from the repair gun's "muzzle"
%muzVec = %obj.getMuzzleVector(%slot);
// muzNVec = normalized muzVec
%muzNVec = VectorNormalize(%muzVec);
%repairRange = DefaultRepairBeam.beamRange;
// scale muzNVec to the range the repair beam can reach
%muzScaled = VectorScale(%muzNVec, %repairRange);
// muzPoint = the actual point of the gun's "muzzle"
%muzPoint = %obj.getMuzzlePoint(%slot);
// rangeEnd = muzzle point + length of beam
%rangeEnd = VectorAdd(%muzPoint, %muzScaled);
// search for just about anything that can be damaged as well as interiors
%searchMasks = $TypeMasks::PlayerObjectType | $TypeMasks::VehicleObjectType |
$TypeMasks::StaticShapeObjectType | $TypeMasks::TurretObjectType | $TypeMasks::InteriorObjectType |
$TypeMasks::ItemObjectType | $TypeMasks::ForceFieldObjectType;
// search for objects within the beam's range that fit the masks above
%scanTarg = ContainerRayCast(%muzPoint, %rangeEnd, %searchMasks, %obj);
// screen out interiors
if (%scanTarg)
{
if (%scanTarg.getType() & $TypeMasks::ForceFieldObjectType)
{
if (%scanTarg.getDataBlock().getName() $= "DeployedForceField2")
%scanTarg = %scanTarg.parent;
else
%scanTarg = "0";
}
}
if(%scanTarg && !(%scanTarg.getType() & $TypeMasks::InteriorObjectType))
{
// a target in range was found
%repTgt = firstWord(%scanTarg);
// is the prospective target damaged?
if(%repTgt.getDamageLevel())
{
// yes, it's damaged
if(%repTgt != %obj.repairing)
{
if(isObject(%obj.repairing))
stopRepairing(%obj);
%obj.repairing = %repTgt;
}
// setting imageLoaded to true sends us to repair state (function onRepair)
%obj.setImageLoaded(%slot, true);
}
else
{
// there is a target in range, but it's not damaged
if(!%obj.errMsgSent)
{
// if the target isn't damaged, send a message to that effect only once
messageClient(%obj.client, 'MsgRepairPackNotDamaged', '\c2Target is not damaged.', %repTgt);
%obj.errMsgSent = true;
}
// if player was repairing something, stop the repairs -- we're done
if(%obj.repairing)
stopRepairing(%obj);
}
}
//AI hack - too many things influence the aiming, so I'm going to force the repair object for bots only
else if (%obj.client.isAIControlled() && isObject(%obj.client.repairObject))
{
%repTgt = %obj.client.repairObject;
%repPoint = %repTgt.getAIRepairPoint();
if (%repPoint $= "0 0 0")
%repPoint = %repTgt.getWorldBoxCenter();
%repTgtVector = VectorNormalize(VectorSub(%muzPoint, %repPoint));
%aimVector = VectorNormalize(VectorSub(%muzPoint, %rangeEnd));
//if the dot product is very close (ie. we're aiming in the right direction)
if (VectorDot(%repTgtVector, %aimVector) > 0.85)
{
//do an LOS to make sure nothing is in the way...
%scanTarg = ContainerRayCast(%muzPoint, %repPoint, %searchMasks, %obj);
if (firstWord(%scanTarg) == %repTgt)
{
// yes, it's damaged
if(isObject(%obj.repairing))
stopRepairing(%obj);
%obj.repairing = %repTgt;
// setting imageLoaded to true sends us to repair state (function onRepair)
%obj.setImageLoaded(%slot, true);
}
}
}
else if(%obj.getDamageLevel())
{
// there is no target in range, but the player is damaged
// check to see if we were repairing something before -- if so, stop repairing old target
if(%obj.repairing != 0)
if(%obj.repairing != %obj)
stopRepairing(%obj);
if(isObject(%obj.repairing))
stopRepairing(%obj);
%obj.repairing = %obj;
// quick, to onRepair!
%obj.setImageLoaded(%slot, true);
}
else
{
// there is no target in range, and the player isn't damaged
if(!%obj.errMsgSent)
{
// send an error message only once
messageClient(%obj.client, 'MsgRepairPackNoTarget', '\c2No target to repair.');
%obj.errMsgSent = true;
}
stopRepairing(%obj);
}
}
function RepairGunImage2::onRepair(%this,%obj,%slot)
{
// this = repairgunimage datablock
// obj = player wielding the repair gun
// slot = weapon slot
if(%obj.getEnergyLevel() <= %this.cutOffEnergy)
{
stopRepairing(%obj);
return;
}
// reset the flag that indicates an error message has been sent
%obj.errMsgSent = false;
%target = %obj.repairing;
if(!%target)
{
// no target -- whoops! never mind
stopRepairing(%obj);
}
else
{
%target.repairedBy = %obj.client; //keep track of who last repaired this item
if(%obj.repairing == %obj)
{
// player is self-repairing
if(%obj.getDamageLevel())
{
if(!%obj.selfRepairing)
{
// no need for a projectile, just send a message and up the repair rate
messageClient(%obj.client, 'MsgRepairPackPlayerSelfRepair', '\c2Repairing self.');
%obj.selfRepairing = true;
startRepairing3(%obj, true);
}
}
else
{
messageClient(%obj.client, 'MsgRepairPackSelfDone', '\c2Repairs completed on self.');
stopRepairing(%obj);
%obj.errMsgSent = true;
}
}
else
{
// make sure we still have a target -- more vector fun!!!
%muzVec = %obj.getMuzzleVector(%slot);
%muzNVec = VectorNormalize(%muzVec);
%repairRange = DefaultRepairBeam.beamRange;
%muzScaled = VectorScale(%muzNVec, %repairRange);
%muzPoint = %obj.getMuzzlePoint(%slot);
%rangeEnd = VectorAdd(%muzPoint, %muzScaled);
%searchMasks = $TypeMasks::PlayerObjectType | $TypeMasks::VehicleObjectType |
$TypeMasks::StaticShapeObjectType | $TypeMasks::TurretObjectType |
$TypeMasks::ItemObjectType | $TypeMasks::ForceFieldObjectType;
//AI hack to help "fudge" the repairing stuff...
if (%obj.client.isAIControlled() && isObject(%obj.client.repairObject) && %obj.client.repairObject == %obj.repairing)
{
%repTgt = %obj.client.repairObject;
%repPoint = %repTgt.getAIRepairPoint();
if (%repPoint $= "0 0 0")
%repPoint = %repTgt.getWorldBoxCenter();
%repTgtVector = VectorNormalize(VectorSub(%muzPoint, %repPoint));
%aimVector = VectorNormalize(VectorSub(%muzPoint, %rangeEnd));
//if the dot product is very close (ie. we're aiming in the right direction)
if (VectorDot(%repTgtVector, %aimVector) > 0.85)
%scanTarg = ContainerRayCast(%muzPoint, %repPoint, %searchMasks, %obj);
}
else
%scanTarg = ContainerRayCast(%muzPoint, %rangeEnd, %searchMasks, %obj);
if (%scanTarg)
{
if (%scanTarg.getType() & $TypeMasks::ForceFieldObjectType)
{
if (%scanTarg.getDataBlock().getName() $= "DeployedForceField2")
{
%pos = getWords(%scanTarg, 1, 3);
%scanTarg = %scanTarg.parent SPC %pos;
}
else
%scanTarg = "0";
}
}
if (%scanTarg)
{
%pos = getWords(%scanTarg, 1, 3);
%obstructMask = $TypeMasks::InteriorObjectType | $TypeMasks::TerrainObjectType;
%obstruction = ContainerRayCast(%muzPoint, %pos, %obstructMask, %obj);
if (%obstruction)
%scanTarg = "0";
}
if (%scanTarg && %scanTarg2)
%scanTarg = %scanTarg2;
if(%scanTarg)
{
// there's still a target out there
%repTgt = firstWord(%scanTarg);
// is the target damaged?
if(%repTgt.getDamageLevel())
{
if(%repTgt != %obj.repairing)
{
// the target is not the same as the one we were just repairing
// stop repairing old target, start repairing new target
stopRepairing(%obj);
if(isObject(%obj.repairing))
stopRepairing(%obj);
%obj.repairing = %repTgt;
// extract the name of what player is repairing based on what it is
// if it's a player, it's the player's name (duh)
// if it's an object, look for a nametag
// if object has no nametag, just say what it is (e.g. generatorLarge)
if(%repTgt.getClassName() $= Player)
%tgtName = getTaggedString(%repTgt.client.name);
else if(%repTgt.getGameName() !$= "")
%tgtName = %repTgt.getGameName();
else
%tgtName = %repTgt.getDatablock().getName();
messageClient(%obj.client, 'MsgRepairPackRepairingObj', '\c2Repairing %1.', %tgtName, %repTgt);
startRepairing3(%obj, false);
}
else
{
// it's the same target as last time
// changed to fix "2 players can't repair same object" bug
if(%obj.repairProjectile == 0)
{
if(%repTgt.getClassName() $= Player)
%tgtName = getTaggedString(%repTgt.client.name);
else if(%repTgt.getGameName() !$= "")
%tgtName = %repTgt.getGameName();
else
%tgtName = %repTgt.getDatablock().getName();
messageClient(%obj.client, 'MsgRepairPackRepairingObj', '\c2Repairing %1.', %tgtName, %repTgt);
startRepairing3(%obj, false);
}
}
}
else
{
%rateOfRepair = %this.repairFactorObject;
if(%repTgt.getClassName() $= Player)
{
%tgtName = getTaggedString(%repTgt.client.name);
%rateOfRepair = %this.repairFactorPlayer;
}
else if(%repTgt.getGameName() !$= "")
%tgtName = %repTgt.getGameName();
else
%tgtName = %repTgt.getDatablock().getName();
if(%repTgt != %obj.repairing)
{
// it isn't the same object we were repairing previously
messageClient(%obj.client, 'MsgRepairPackNotDamaged', '\c2%1 is not damaged.', %tgtName);
}
else
{
// same target, but not damaged -- we must be done
messageClient(%obj.client, 'MsgRepairPackDone', '\c2Repairs completed.');
Game.objectRepaired(%repTgt, %tgtName);
}
%obj.errMsgSent = true;
stopRepairing(%obj);
}
}
else
{
// whoops, we lost our target
messageClient(%obj.client, 'MsgRepairPackLostTarget', '\c2Repair target no longer in range.');
stopRepairing(%obj);
}
}
}
}
function startRepairing3(%player, %self)
{
// %player = the player who was using the repair pack
// %self = boolean -- is player repairing him/herself?
if(%self)
{
// one repair, hold the projectile
%player.setRepairRate(%player.getRepairRate() + RepairGunImage2.repairFactorPlayer);
%player.selfRepairing = true;
%player.repairingRate = RepairGunImage2.repairFactorPlayer;
}
else
{
//if(%player.repairing.beingRepaired $= "")
// %player.repairing.beingRepaired = 1;
//else
// %player.repairing.beingRepaired++;
//AI hack...
if (%player.client.isAIControlled() && %player.client.repairObject == %player.repairing)
{
%initialPosition = %player.getMuzzlePoint($WeaponSlot);
%initialDirection = VectorSub(%initialPosition, %player.repairing.getWorldBoxCenter());
}
else
{
%initialDirection = %player.getMuzzleVector($WeaponSlot);
%initialPosition = %player.getMuzzlePoint($WeaponSlot);
}
if(%player.repairing.getClassName() $= Player)
%repRate = RepairGunImage2.repairFactorPlayer;
else
%repRate = RepairGunImage2.repairFactorObject;
%player.repairing.setRepairRate(%player.repairing.getRepairRate() + %repRate);
%player.repairingRate = %repRate;
if (%player.repairing.getDataBlock().getName() $= "DeployedForceField")
%targetObject = %player.repairing.field;
else
%targetObject = %player.repairing;
%player.repairProjectile = new RepairProjectile() {
dataBlock = DefaultRepairBeam;
initialDirection = %initialDirection;
initialPosition = %initialPosition;
sourceObject = %player;
sourceSlot = $WeaponSlot;
targetObject = %targetObject;
};
MissionCleanup.add(%player.repairProjectile);
}
}
function RepairGunImage2::onDeactivate(%this,%obj,%slot)
{
stopRepairing(%obj);
}
function RepairGunImage2::onPickup(%this, %obj, %shape, %amount)
{
// created to prevent console errors
}
-----------------------------------------------------------------------------
Sniper Repair Rifle
Step #1
// ------------------------------------------
// inventoryHud.cs
// ------------------------------------------
1a)
$InvWeapon[13] = "Repair Rifle";
$NameToInv["Repair Rifle"] = "RepairRifle";
1b) In function InventoryScreen::updateHud, These lines:
else if ( "SniperRifle" $= $NameToInv[%client.favorites[getField( %client.weaponIndex,%i )]] )
{
%noSniperRifle = false;
%packList = "noSelect\tEnergy Pack\tEnergy Pack must be used when \tLaser Rifle is selected!";
%client.favorites[getField(%client.packIndex,0)] = "Energy Pack";
}
should become:
else if ( "SniperRifle" $= $NameToInv[%client.favorites[getField( %client.weaponIndex,%i )]] )
{
%noSniperRifle = false;
%packList = "noSelect\tEnergy Pack\tEnergy Pack must be used when \tLaser Rifle is selected!";
%client.favorites[getField(%client.packIndex,0)] = "Energy Pack";
}
else if ( "RepairRifle" $= $NameToInv[%client.favorites[getField( %client.weaponIndex,%i )]] )
{
%noSniperRifle = false;
%packList = "noSelect\tEnergy Pack\tEnergy Pack must be used when \tRepair Rifle is selected!";
%client.favorites[getField(%client.packIndex,0)] = "Energy Pack";
}
1c)
function checkPackValidity(%pack, %equipment, %item)
{
%require["Repair Rifle"] = "Pack" TAB "Energy Pack";
}
Step #2
// ------------------------------------------
// weapons.cs
// ------------------------------------------
$WeaponsHudData[13, bitmapName] = "gui/hud_new_packrepair";
$WeaponsHudData[13, itemDataName] = "RepairRifle";
$WeaponsHudCount = 14;
exec("scripts/weapons/RepairRifle.cs");
Step #3
// ------------------------------------------
// inventory.cs
// ------------------------------------------
3a)
function ShapeBase::hasAmmo( %this, %weapon )
{
switch$ ( %weapon )
{
case RepairRifle:
return( %this.getInventory( EnergyPack ) );
}
}
3b)
function ShapeBase::clearInventory(%this)
{
%this.setInventory(RepairRifle,0);
}
3c)
function serverCmdGiveAll(%client)
{
if($TestCheats)
{
%player.setInventory(RepairRifle,1);
}
}
Step #4
// ------------------------------------------
// player.cs
// ------------------------------------------
4a) Add this to player datablocks. Recommend only Snipers/Lights.
max [RepairRifle] = 1;
4b) Add these to function armor::applyConcussion
if( %weaps[13] = %player.getInventory("RepairRifle") > 0 ) %numWeapons++;
and
case 12:
%player.use("RepairRifle");
Step #5
// ------------------------------------------
// hud.cs
// ------------------------------------------
Skip this if you use any server side reticle fixes.
function clientCmdSetWeaponsHudActive(%slot)
{
weaponsHud.setActiveWeapon(%slot);
switch$($WeaponNames[%slot])
{
case "RepairRifle":
reticleHud.setBitmap("gui/hud_ret_targlaser");
reticleFrameHud.setVisible(false);
}
}
Step #6
// ------------------------------------------
// OptionsDlg.cs
// ------------------------------------------
$RemapName[$RemapCount] = "RepairRifle";
$RemapCmd[$RemapCount] = "useRepairRifle";
$RemapCount++;
Step #7
// ------------------------------------------
// ControlDefaults.cs
// ------------------------------------------
function useRepairRifle( %val )
{
if ( %val )
use( RepairRifle );
}
Step #8
// ------------------------------------------
// RepairRifle.cs
// ------------------------------------------
Create a file called RepairRifle.cs in your weapons directory. Add all this into it:
//--------------------------------------------------------------------------
// Repair Rifle
//
//--------------------------------------------------------------------------
datablock EffectProfile(RepairRifleSwitchEffect)
{
effectname = "weapons/generic_switch";
minDistance = 2.5;
maxDistance = 2.5;
};
datablock EffectProfile(RepairRiflePaintEffect)
{
effectname = "weapons/targetinglaser_paint";
minDistance = 2.5;
maxDistance = 2.5;
};
datablock AudioProfile(RepairRifleSwitchSound)
{
filename = "fx/weapons/generic_switch.wav";
description = AudioClosest3d;
preload = true;
effect = RepairRifleSwitchEffect;
};
datablock AudioProfile(RepairRiflePaintSound)
{
filename = "fx/weapons/targetinglaser_paint.wav";
description = CloseLooping3d;
preload = true;
effect = RepairRiflePaintEffect;
};
//--------------------------------------
// Projectile
//--------------------------------------
datablock TargetProjectileData(RepairLaser)
{
directDamage = 0.0;
hasDamageRadius = false;
indirectDamage = 0.0;
damageRadius = 0.0;
velInheritFactor = 1.0;
maxRifleRange = 1000;
beamColor = "1.0 0.1 0.1";
startBeamWidth = 0.20;
pulseBeamWidth = 0.15;
beamFlareAngle = 3.0;
minFlareSize = 0.0;
maxFlareSize = 400.0;
pulseSpeed = 6.0;
pulseLength = 0.150;
textureName[0] = "special/nonlingradient";
textureName[1] = "special/redflare";
textureName[2] = "special/pulse";
textureName[3] = "special/expFlare";
textureName[4] = "special/redbump2";
};
//--------------------------------------
// Rifle and item...
//--------------------------------------
datablock ItemData(RepairRifle)
{
className = Weapon;
catagory = "Spawn Items";
shapeFile = "weapon_targeting.dts";
image = RepairRifleImage;
mass = 1;
elasticity = 0.2;
friction = 0.6;
pickupRadius = 2;
pickUpName = "a repair rifle";
computeCRC = true;
};
datablock ShapeBaseImageData(RepairRifleImage)
{
className = WeaponImage;
shapeFile = "weapon_repair.dts";
offset = "0 0 0";
item = RepairRifle;
showname = "Repair Rifle";
usesEnergy = true;
minEnergy = 3;
cutOffEnergy = 3.1;
emap = true;
repairFactorPlayer = 0.001; // <--- attention DaveG!
repairFactorObject = 0.002; // <--- attention DaveG!
stateName[0] = "Activate";
stateTransitionOnTimeout[0] = "ActivateReady";
stateTimeoutValue[0] = 0.25;
stateName[1] = "ActivateReady";
stateScript[1] = "onActivateReady";
stateSpinThread[1] = Stop;
stateTransitionOnAmmo[1] = "Ready";
stateTransitionOnNoAmmo[1] = "ActivateReady";
stateName[2] = "Ready";
stateSpinThread[2] = Stop;
stateTransitionOnNoAmmo[2] = "Deactivate";
stateTransitionOnTriggerDown[2] = "Validate";
stateName[3] = "Validate";
stateTransitionOnTimeout[3] = "Validate";
stateTimeoutValue[3] = 0.2;
stateEnergyDrain[3] = 3;
stateSpinThread[3] = SpinUp;
stateScript[3] = "onValidate";
stateIgnoreLoadedForReady[3] = true;
stateTransitionOnLoaded[3] = "Repair";
stateTransitionOnNoAmmo[3] = "Deactivate";
stateTransitionOnTriggerUp[3] = "Deactivate";
stateName[4] = "Repair";
stateSound[4] = RepairPackFireSound;
stateScript[4] = "onRepair";
stateSpinThread[4] = FullSpeed;
stateAllowImageChange[4] = false;
stateSequence[4] = "activate";
stateFire[4] = true;
stateEnergyDrain[4] = 9;
stateTimeoutValue[4] = 0.2;
stateTransitionOnTimeOut[4] = "Repair";
stateTransitionOnNoAmmo[4] = "Deactivate";
stateTransitionOnTriggerUp[4] = "Deactivate";
stateTransitionOnNotLoaded[4] = "Validate";
stateName[5] = "Deactivate";
stateScript[5] = "onDeactivate";
stateSpinThread[5] = SpinDown;
stateSequence[5] = "activate";
stateDirection[5] = false;
stateTimeoutValue[5] = 0.2;
stateTransitionOnTimeout[5] = "ActivateReady";
};
function RepairRifleImage::onUnmount(%this,%obj,%slot)
{
// called when player switches to another weapon
// stop repairing whatever player was repairing
if(%obj.repairing)
stopRepairing(%obj);
%obj.setImageTrigger(%slot, false);
// "turn off" the repair pack -- player needs to hit the "pack" key to
// activate the repair gun again
}
function RepairRifleImage::onActivateReady(%this,%obj,%slot)
{
%obj.errMsgSent = false;
%obj.selfRepairing = false;
%obj.repairing = 0;
%obj.setImageLoaded(%slot, false);
}
function RepairRifleImage::onValidate(%this,%obj,%slot)
{
// this = repairgunimage datablock
// obj = player wielding the repair gun
// slot = weapon slot
if(!%obj.hasEnergyPack)
{
// siddown Junior, you can't use it
stopRepairing(%obj);
return;
}
if(%obj.getEnergyLevel() <= %this.cutOffEnergy)
{
stopRepairing(%obj);
return;
}
%repGun = %obj.getMountedImage(%slot);
// muzVec is the vector coming from the repair gun's "muzzle"
%muzVec = %obj.getMuzzleVector(%slot);
// muzNVec = normalized muzVec
%muzNVec = VectorNormalize(%muzVec);
%repairRange = RepairLaser.maxRifleRange;
// scale muzNVec to the range the repair beam can reach
%muzScaled = VectorScale(%muzNVec, %repairRange);
// muzPoint = the actual point of the gun's "muzzle"
%muzPoint = %obj.getMuzzlePoint(%slot);
// rangeEnd = muzzle point + length of beam
%rangeEnd = VectorAdd(%muzPoint, %muzScaled);
// search for just about anything that can be damaged as well as interiors
%searchMasks = $TypeMasks::PlayerObjectType | $TypeMasks::VehicleObjectType |
$TypeMasks::StaticShapeObjectType | $TypeMasks::TurretObjectType | $TypeMasks::InteriorObjectType |
$TypeMasks::ItemObjectType | $TypeMasks::ForceFieldObjectType;
// search for objects within the beam's range that fit the masks above
%scanTarg = ContainerRayCast(%muzPoint, %rangeEnd, %searchMasks, %obj);
// screen out interiors
if (%scanTarg)
{
if (%scanTarg.getType() & $TypeMasks::ForceFieldObjectType)
{
if (%scanTarg.getDataBlock().getName() $= "DeployedForceField2")
%scanTarg = %scanTarg.parent;
else
%scanTarg = "0";
}
}
if(%scanTarg && !(%scanTarg.getType() & $TypeMasks::InteriorObjectType))
{
// a target in range was found
%repTgt = firstWord(%scanTarg);
// is the prospective target damaged?
if(%repTgt.getDamageLevel())
{
// yes, it's damaged
if(%repTgt != %obj.repairing)
{
if(isObject(%obj.repairing))
stopRepairing(%obj);
%obj.repairing = %repTgt;
}
// setting imageLoaded to true sends us to repair state (function onRepair)
%obj.setImageLoaded(%slot, true);
}
else
{
// there is a target in range, but it's not damaged
if(!%obj.errMsgSent)
{
// if the target isn't damaged, send a message to that effect only once
messageClient(%obj.client, 'MsgRepairPackNotDamaged', '\c2Target is not damaged.', %repTgt);
%obj.errMsgSent = true;
}
// if player was repairing something, stop the repairs -- we're done
if(%obj.repairing)
stopRepairing(%obj);
}
}
//AI hack - too many things influence the aiming, so I'm going to force the repair object for bots only
else if (%obj.client.isAIControlled() && isObject(%obj.client.repairObject))
{
%repTgt = %obj.client.repairObject;
%repPoint = %repTgt.getAIRepairPoint();
if (%repPoint $= "0 0 0")
%repPoint = %repTgt.getWorldBoxCenter();
%repTgtVector = VectorNormalize(VectorSub(%muzPoint, %repPoint));
%aimVector = VectorNormalize(VectorSub(%muzPoint, %rangeEnd));
//if the dot product is very close (ie. we're aiming in the right direction)
if (VectorDot(%repTgtVector, %aimVector) > 0.85)
{
//do an LOS to make sure nothing is in the way...
%scanTarg = ContainerRayCast(%muzPoint, %repPoint, %searchMasks, %obj);
if (firstWord(%scanTarg) == %repTgt)
{
// yes, it's damaged
if(isObject(%obj.repairing))
stopRepairing(%obj);
%obj.repairing = %repTgt;
// setting imageLoaded to true sends us to repair state (function onRepair)
%obj.setImageLoaded(%slot, true);
}
}
}
else if(%obj.getDamageLevel())
{
// there is no target in range, but the player is damaged
// check to see if we were repairing something before -- if so, stop repairing old target
if(%obj.repairing != 0)
if(%obj.repairing != %obj)
stopRepairing(%obj);
if(isObject(%obj.repairing))
stopRepairing(%obj);
%obj.repairing = %obj;
// quick, to onRepair!
%obj.setImageLoaded(%slot, true);
}
else
{
// there is no target in range, and the player isn't damaged
if(!%obj.errMsgSent)
{
// send an error message only once
messageClient(%obj.client, 'MsgRepairPackNoTarget', '\c2No target to repair.');
%obj.errMsgSent = true;
}
stopRepairing(%obj);
}
}
function RepairRifleImage::onRepair(%this,%obj,%slot)
{
// this = repairgunimage datablock
// obj = player wielding the repair gun
// slot = weapon slot
if(!%obj.hasEnergyPack)
{
// siddown Junior, you can't use it
stopRepairing(%obj);
return;
}
if(%obj.getEnergyLevel() <= %this.cutOffEnergy)
{
stopRepairing(%obj);
return;
}
// reset the flag that indicates an error message has been sent
%obj.errMsgSent = false;
%target = %obj.repairing;
if(!%target)
{
// no target -- whoops! never mind
stopRepairing(%obj);
}
else
{
%target.repairedBy = %obj.client; //keep track of who last repaired this item
if(%obj.repairing == %obj)
{
// player is self-repairing
if(%obj.getDamageLevel())
{
if(!%obj.selfRepairing)
{
// no need for a projectile, just send a message and up the repair rate
messageClient(%obj.client, 'MsgRepairPackPlayerSelfRepair', '\c2Repairing self.');
%obj.selfRepairing = true;
startRepairing2(%obj, true);
}
}
else
{
messageClient(%obj.client, 'MsgRepairPackSelfDone', '\c2Repairs completed on self.');
stopRepairing(%obj);
%obj.errMsgSent = true;
}
}
else
{
// make sure we still have a target -- more vector fun!!!
%muzVec = %obj.getMuzzleVector(%slot);
%muzNVec = VectorNormalize(%muzVec);
%repairRange = RepairLaser.maxRifleRange;
%muzScaled = VectorScale(%muzNVec, %repairRange);
%muzPoint = %obj.getMuzzlePoint(%slot);
%rangeEnd = VectorAdd(%muzPoint, %muzScaled);
%searchMasks = $TypeMasks::PlayerObjectType | $TypeMasks::VehicleObjectType |
$TypeMasks::StaticShapeObjectType | $TypeMasks::TurretObjectType |
$TypeMasks::ItemObjectType | $TypeMasks::ForceFieldObjectType;
//AI hack to help "fudge" the repairing stuff...
if (%obj.client.isAIControlled() && isObject(%obj.client.repairObject) && %obj.client.repairObject == %obj.repairing)
{
%repTgt = %obj.client.repairObject;
%repPoint = %repTgt.getAIRepairPoint();
if (%repPoint $= "0 0 0")
%repPoint = %repTgt.getWorldBoxCenter();
%repTgtVector = VectorNormalize(VectorSub(%muzPoint, %repPoint));
%aimVector = VectorNormalize(VectorSub(%muzPoint, %rangeEnd));
//if the dot product is very close (ie. we're aiming in the right direction)
if (VectorDot(%repTgtVector, %aimVector) > 0.85)
%scanTarg = ContainerRayCast(%muzPoint, %repPoint, %searchMasks, %obj);
}
else
%scanTarg = ContainerRayCast(%muzPoint, %rangeEnd, %searchMasks, %obj);
if (%scanTarg)
{
if (%scanTarg.getType() & $TypeMasks::ForceFieldObjectType)
{
if (%scanTarg.getDataBlock().getName() $= "DeployedForceField2")
{
%pos = getWords(%scanTarg, 1, 3);
%scanTarg = %scanTarg.parent SPC %pos;
}
else
%scanTarg = "0";
}
}
if (%scanTarg)
{
%pos = getWords(%scanTarg, 1, 3);
%obstructMask = $TypeMasks::InteriorObjectType | $TypeMasks::TerrainObjectType;
%obstruction = ContainerRayCast(%muzPoint, %pos, %obstructMask, %obj);
if (%obstruction)
%scanTarg = "0";
}
if (%scanTarg && %scanTarg2)
%scanTarg = %scanTarg2;
if(%scanTarg)
{
// there's still a target out there
%repTgt = firstWord(%scanTarg);
// is the target damaged?
if(%repTgt.getDamageLevel())
{
if(%repTgt != %obj.repairing)
{
// the target is not the same as the one we were just repairing
// stop repairing old target, start repairing new target
stopRepairing(%obj);
if(isObject(%obj.repairing))
stopRepairing(%obj);
%obj.repairing = %repTgt;
// extract the name of what player is repairing based on what it is
// if it's a player, it's the player's name (duh)
// if it's an object, look for a nametag
// if object has no nametag, just say what it is (e.g. generatorLarge)
if(%repTgt.getClassName() $= Player)
%tgtName = getTaggedString(%repTgt.client.name);
else if(%repTgt.getGameName() !$= "")
%tgtName = %repTgt.getGameName();
else
%tgtName = %repTgt.getDatablock().getName();
messageClient(%obj.client, 'MsgRepairPackRepairingObj', '\c2Repairing %1.', %tgtName, %repTgt);
startRepairing2(%obj, false);
}
else
{
// it's the same target as last time
// changed to fix "2 players can't repair same object" bug
if(%obj.repairProjectile == 0)
{
if(%repTgt.getClassName() $= Player)
%tgtName = getTaggedString(%repTgt.client.name);
else if(%repTgt.getGameName() !$= "")
%tgtName = %repTgt.getGameName();
else
%tgtName = %repTgt.getDatablock().getName();
messageClient(%obj.client, 'MsgRepairPackRepairingObj', '\c2Repairing %1.', %tgtName, %repTgt);
startRepairing2(%obj, false);
}
}
}
else
{
%rateOfRepair = %this.repairFactorObject;
if(%repTgt.getClassName() $= Player)
{
%tgtName = getTaggedString(%repTgt.client.name);
%rateOfRepair = %this.repairFactorPlayer;
}
else if(%repTgt.getGameName() !$= "")
%tgtName = %repTgt.getGameName();
else
%tgtName = %repTgt.getDatablock().getName();
if(%repTgt != %obj.repairing)
{
// it isn't the same object we were repairing previously
messageClient(%obj.client, 'MsgRepairPackNotDamaged', '\c2%1 is not damaged.', %tgtName);
}
else
{
// same target, but not damaged -- we must be done
messageClient(%obj.client, 'MsgRepairPackDone', '\c2Repairs completed.');
Game.objectRepaired(%repTgt, %tgtName);
}
%obj.errMsgSent = true;
stopRepairing(%obj);
}
}
else
{
// whoops, we lost our target
messageClient(%obj.client, 'MsgRepairPackLostTarget', '\c2Repair target no longer in range.');
stopRepairing(%obj);
}
}
}
}
function startRepairing2(%player, %self)
{
// %player = the player who was using the repair pack
// %self = boolean -- is player repairing him/herself?
if(%self)
{
// one repair, hold the projectile
%player.setRepairRate(%player.getRepairRate() + RepairRifleImage.repairFactorPlayer);
%player.selfRepairing = true;
%player.repairingRate = RepairRifleImage.repairFactorPlayer;
}
else
{
//if(%player.repairing.beingRepaired $= "")
// %player.repairing.beingRepaired = 1;
//else
// %player.repairing.beingRepaired++;
//AI hack...
if (%player.client.isAIControlled() && %player.client.repairObject == %player.repairing)
{
%initialPosition = %player.getMuzzlePoint($WeaponSlot);
%initialDirection = VectorSub(%initialPosition, %player.repairing.getWorldBoxCenter());
}
else
{
%initialDirection = %player.getMuzzleVector($WeaponSlot);
%initialPosition = %player.getMuzzlePoint($WeaponSlot);
}
if(%player.repairing.getClassName() $= Player)
%repRate = RepairRifleImage.repairFactorPlayer;
else
%repRate = RepairRifleImage.repairFactorObject;
%player.repairing.setRepairRate(%player.repairing.getRepairRate() + %repRate);
%player.repairingRate = %repRate;
%player.repairProjectile = new TargetProjectile() {
dataBlock = RepairLaser;
initialDirection = %initialDirection;
initialPosition = %initialPosition;
sourceObject = %player;
sourceSlot = $WeaponSlot;
};
MissionCleanup.add(%player.repairProjectile);
}
}
function RepairRifleImage::onDeactivate(%this,%obj,%slot)
{
stopRepairing(%obj);
}
function RepairRifleImage::onPickup(%this, %obj, %shape, %amount)
{
// created to prevent console errors
}