TribesReplay/base/scripts/CTFGame.cs
Robert MacGregor 7f1fccfdff v23669 (06/25/01):
- (bug fix) Vehicles and deployables now properly explode if they are destroyed while someone is repairing them.

- (bug fix) Sniper laser shots no longer create water splash effects if hitting ground near the water.

- (bug fix) Immersion iForce force feedback mouse is now working properly again.

- (bug fix) The "flag jumping" bug is now fixed. When a flag lands after being dropped, it will stay put when it slides to rest.

- (bug fix) Fixed a situation where closing tribe or player tags on the Browser out of order would cause a disconnect with the database server.

- (bug fix) Players can no longer fire, place mines, place grenades, or place beacons when inside a force field.

- (bug fix) Fixed bug where modifier keys (specifically SHIFT) bound to actions in the game would still cause those actions when typing in a text edit control in-game (such as the chat entry)

- (bug fix) Fixed a bug that could cause a player to drop to desktop when attempting to join a game which was in the process of cycling missions.

- (bug fix) Fixed a Radeon video card issue which could occur if the desktop color bit-depth was different than the color bit-depth that the player was using in the game.

- (bug fix) You won't try to fade into a vehicle that was destroyed after you purchased it, but before you had actually tported to the seat.

- (bug fix) Minor change in the MPB explosion so that the turret part of the MPB doesn't seem to hover in place for a split-second during the explosion.

- (bug fix) Bomber bombs now tumble properly and won't seem to disappear when falling.

- (bug fix) Fixed a rare problem that could cause a client crash while the server is resetting.

- (bug fix) Fixed a problem with the ELF gun effect that was causing hangs.

- (bug fix) Telnet can now be used to set passwords for PURE servers so that they can be used for match games. (command line option...see the post in T2FAQs called "How do I TELNET INTO A PURE SERVER?" for more information on how to use this ability.)

- (bug fix) The "cloning" issue (where players could clone themselves by dying, going to the CC with the CC camera showing themselves, spawn, and flicker back and forth to the CC) is now fixed and no longer occurs.

- (bug fix) Another "cloning" issue which occurred when the Tourney Admin would switch teams for players is fixed and no longer occurs.

- (bug fix) Fixed a bug where, when a client joins a server where the client does not have the map being run on the server, the client hangs while loading. This now elegantly exits instead of hanging.

- (bug fix) Fixed an issue where the last few characters of the Server Info dialog would be cut off.

- (bug fix) Fixed a situation where a blank error box could occur if CD key not entered properly when creating an account.

- (bug fix) Sensor rings will no longer show up on the Command Circuit if the generators are not powered.

- (bug fix) There was a rare bug where, if a player was standing in a force field's position when that force field went from a depowered to powered state (in otherwords, if the gens were repaired while he stood in the FF position), then the player would be stuck forever. If this case occurs, that player will now be destroyed.

- (improvement) The "redjack" icon has been removed and new network throughput graphs have been implemented to better help players troubleshoot their net settings. Additionally, a more accurate and complete set of network presets is available. (See details below under "NETWORK SETTINGS" for more information.)

- (improvement) The pure server concept is now implemented. Pure servers only allow regulation scripts and maps to be run on the server (no restrictions yet on the client), thus ensuring that anyone that joins a "base" server is playing the game as it was designed by Dynamix. MODs are still easily joined, but players can be assured that a "Base" game is really a "Base" game now. (Any game with server or rules mods that is not actually named as a new MOD will be described as "variant" instead of "base" on the master server list.) NOTE: See "PURE SERVER" below for more information on this.

- (improvement) Old Password is now required in order to enter a New Password when editing your account.

- (improvement) Password handling is different now in order to make it more difficult for people to casually find a password on a hard drive.

- (improvement) Made more room for player names to display on the Server Info box.

- (improvement) Bomber and Tank now have separate energy capacitors for their turret weapons. This energy pool is completely separate from the energy pool that the thrusters and force shields use. (Gunner energy is displayed as a second bar below the regular vehicle energy and is orangish in color.)

- (improvement) Vertical thrusters on air vehicles are now more efficient to enable better takeoffs from ground level.

- (improvement) Changed team damage OFF to include friendly turret fire and vehicle fire. (In otherwords, if Team Damage is OFF, then turrets fire and vehicle fire will not affect friendly units.)

- (improvement) Splash damage no longer falls off so dramatically with distance. You will find that area effect (explosion) weapons now are more effective within their damage area.

- (improvement) While in Tournament mode, and while in observer mode at the beginning of a match (before teams have been selected), players will now be able to chat with one another.

- (improvement) Added observer points to Tombstone (it previously had none).

- (improvement) Grenades tossing is slightly improved. The grenades will throw farther with less time spent pressing the grenade key. (They still have the exact same minimum and maximum throwing distances, it's just easier to throw it out to max range now.)

- (community) Player histories are now accurate.

- (community) Preferences in the FORUMs should be fixed now so that they stay in existence. The sort is the only exception. That will reformat each time you enter the FORUM and you will need to select whichever sort your prefer at that time.
2017-07-17 23:22:55 -04:00

1004 lines
36 KiB
C#

// DisplayName = Capture the Flag
//--- GAME RULES BEGIN ---
//Prevent enemy from capturing your flag
//Score one point for grabbing the enemy's flag
//To capture, your flag must be at its stand
//Score 100 points each time enemy flag is captured
//--- GAME RULES END ---
//exec the AI scripts
exec("scripts/aiCTF.cs");
package CTFGame {
function Generator::onDestroyed(%data, %obj)
{
if (Game.testGenDestroyed(%obj))
Game.awardScoreGenDestroy(%obj.lastDamagedBy);
}
function Flag::objectiveInit(%data, %flag)
{
if (!%flag.isTeamSkinned)
{
%pos = %flag.getTransform();
%group = %flag.getGroup();
}
%flag.originalPosition = %flag.getTransform();
%flag.isHome = true;
%flag.carrier = "";
setTargetSkin(%flag.getTarget(), $TeamSkin[%flag.team]);
setTargetSensorGroup(%flag.getTarget(), %flag.team);
setTargetAlwaysVisMask(%flag.getTarget(), 0x7);
setTargetRenderMask(%flag.getTarget(), getTargetRenderMask(%flag.getTarget()) | 0x2);
%flag.scopeWhenSensorVisible(true);
$flagStatus[%flag.team] = "<At Base>";
//Point the flag and stand at each other
%group = %flag.getGroup();
%count = %group.getCount();
%flag.stand = "";
for(%i = 0; %i < %count; %i++)
{
%this = %group.getObject(%i);
if((%this.getClassName() !$= "InteriorInstance") && (%this.getClassName() !$= "SimGroup"))
{
if(%this.getDataBlock().getName() $= "ExteriorFlagStand")
{
%flag.stand = %this;
%this.flag = %flag;
}
}
}
// set the nametag on the target
setTargetName(%flag.getTarget(), $teamName[%flag.team]);
// create a marker on this guy
%flag.waypoint = new MissionMarker() {
position = %flag.getTransform();
dataBlock = "FlagMarker";
};
MissionCleanup.add(%flag.waypoint);
// create a target for this (there is no MissionMarker::onAdd script call)
%target = createTarget(%flag.waypoint, $teamName[%flag.team], "", "", 'Base', %flag.team, 0);
setTargetAlwaysVisMask(%target, 0xffffffff);
//store the flag in an array
$TeamFlag[%flag.team] = %flag;
}
};
//-- tracking ---
// .deaths .kills .genDefends .carrierKills .flagDefends .escortAssists .suicides .teamKills .turretKills .flagCaps
// .flagReturns .genDestroys .genRepairs
function CTFGame::initGameVars(%game)
{
%game.SCORE_PER_SUICIDE = -1;
%game.SCORE_PER_TEAMKILL = -1;
%game.SCORE_PER_DEATH = -1;
%game.SCORE_PER_KILL = 1;
%game.SCORE_PER_PLYR_FLAG_CAP = 3;
%game.SCORE_PER_TEAM_FLAG_CAP = 100;
%game.SCORE_PER_TEAM_FLAG_TOUCH = 1;
%game.SCORE_PER_GEN_DESTROY = 2;
%game.SCORE_PER_ESCORT_ASSIST = 1;
%game.SCORE_PER_TURRET_KILL = 1;
%game.SCORE_PER_FLAG_DEFEND = 1;
%game.SCORE_PER_CARRIER_KILL = 1;
%game.SCORE_PER_FLAG_RETURN = 1;
%game.SCORE_PER_GEN_DEFEND = 1;
%game.SCORE_PER_GEN_REPAIR = 1;
%game.FLAG_RETURN_DELAY = 45 * 1000; //45 seconds
%game.TIME_CONSIDERED_FLAGCARRIER_THREAT = 3 * 1000; //after damaging enemy flag carrier
%game.RADIUS_GEN_DEFENSE = 20; //meters
%game.RADIUS_FLAG_DEFENSE = 20; //meters
%game.TOUCH_DELAY_MS = 20000; //20 secs
%game.fadeTimeMS = 2000;
%game.notifyMineDist = 7.5;
%game.stalemate = false;
%game.stalemateObjsVisible = false;
%game.stalemateTimeMS = 60000;
%game.stalemateFreqMS = 15000;
%game.stalemateDurationMS = 6000;
}
//--------------------------------------------------------------------------
// need to have this for the corporate maps which could not be fixed
function SimObject::clearFlagWaypoints(%this)
{
}
function WayPoint::clearFlagWaypoints(%this)
{
logEcho("Removing flag waypoint: " @ %this);
if(%this.nameTag $= "Flag")
%this.delete();
}
function SimGroup::clearFlagWaypoints(%this)
{
for(%i = %this.getCount() - 1; %i >= 0; %i--)
%this.getObject(%i).clearFlagWaypoints();
}
//--------------------------------------------------------------------------
function CTFGame::missionLoadDone(%game)
{
//default version sets up teams - must be called first...
DefaultGame::missionLoadDone(%game);
for(%i = 1; %i < (%game.numTeams + 1); %i++)
$teamScore[%i] = 0;
// remove
MissionGroup.clearFlagWaypoints();
//reset some globals, just in case...
$dontScoreTimer[1] = false;
$dontScoreTimer[2] = false;
}
function CTFGame::playerTouchFlag(%game, %player, %flag)
{
%client = %player.client;
if ((%flag.carrier $= "") && (%player.getState() !$= "Dead"))
{
//flag isn't held and has been touched by a live player
if (%client.team == %flag.team)
%game.playerTouchOwnFlag(%player, %flag);
else
%game.playerTouchEnemyFlag(%player, %flag);
}
// toggle visibility of the flag
setTargetRenderMask(%flag.waypoint.getTarget(), %flag.isHome ? 0 : 1);
}
function CTFGame::playerTouchOwnFlag(%game, %player, %flag)
{
if(%flag.isHome)
{
if (%player.holdingFlag !$= "")
%game.flagCap(%player);
}
else
%game.flagReturn(%flag, %player);
//call the AI function
%game.AIplayerTouchOwnFlag(%player, %flag);
}
function CTFGame::playerTouchEnemyFlag(%game, %player, %flag)
{
%client = %player.client;
%player.holdingFlag = %flag; //%player has this flag
%flag.carrier = %player; //this %flag is carried by %player
%player.mountImage(FlagImage, $FlagSlot, true, $teamSkin[%flag.team]);
%game.playerGotFlagTarget(%player);
//only cancel the return timer if the player is in bounds...
if (!%client.outOfBounds)
{
cancel($FlagReturnTimer[%flag]);
$FlagReturnTimer[%flag] = "";
}
//if this flag was "at home", see if both flags have now been taken
if (%flag.isHome)
{
// tiebreaker score
game.awardScoreFlagTouch( %client, %flag );
%startStalemate = false;
if ($TeamFlag[1] == %flag)
%startStalemate = !$TeamFlag[2].isHome;
else
%startStalemate = !$TeamFlag[1].isHome;
if (%startStalemate)
%game.stalemateSchedule = %game.schedule(%game.stalemateTimeMS, beginStalemate);
}
%flag.hide(true);
%flag.startFade(0, 0, false);
%flag.isHome = false;
if(%flag.stand)
%flag.stand.getDataBlock().onFlagTaken(%flag.stand);//animate, if exterior stand
$flagStatus[%flag.team] = %client.nameBase;
messageTeamExcept(%client, 'MsgCTFFlagTaken', '\c2Teammate %1 took the %2 flag.~wfx/misc/flag_snatch.wav', %client.name, $teamName[%flag.team], %flag.team, %client.nameBase);
messageTeam(%flag.team, 'MsgCTFFlagTaken', '\c2Your flag has been taken by %1!~wfx/misc/flag_taken.wav',%client.name, 0, %flag.team, %client.nameBase);
messageTeam(0, 'MsgCTFFlagTaken', '\c2%1 took the %2 flag.~wfx/misc/flag_snatch.wav', %client.name, $teamName[%flag.team], %flag.team, %client.nameBase);
messageClient(%client, 'MsgCTFFlagTaken', '\c2You took the %2 flag.~wfx/misc/flag_snatch.wav', %client.name, $teamName[%flag.team], %flag.team, %client.nameBase);
logEcho(%client.nameBase@" (pl "@%player@"/cl "@%client@") took team "@%flag.team@" flag");
//call the AI function
%game.AIplayerTouchEnemyFlag(%player, %flag);
//if the player is out of bounds, then in 3 seconds, it should be thrown back towards the in bounds area...
if (%client.outOfBounds)
%game.schedule(3000, "boundaryLoseFlag", %player);
}
function CTFGame::playerGotFlagTarget(%game, %player)
{
%player.scopeWhenSensorVisible(true);
%target = %player.getTarget();
setTargetRenderMask(%target, getTargetRenderMask(%target) | 0x2);
if(%game.stalemateObjsVisible)
setTargetAlwaysVisMask(%target, 0x7);
}
function CTFGame::playerLostFlagTarget(%game, %player)
{
%player.scopeWhenSensorVisible(false);
%target = %player.getTarget();
setTargetRenderMask(%target, getTargetRenderMask(%target) & ~0x2);
// clear his always vis target mask
setTargetAlwaysVisMask(%target, (1 << getTargetSensorGroup(%target)));
}
function CTFGame::playerDroppedFlag(%game, %player)
{
%client = %player.client;
%flag = %player.holdingFlag;
%game.playerLostFlagTarget(%player);
%player.holdingFlag = ""; //player isn't holding a flag anymore
%flag.carrier = ""; //flag isn't held anymore
$flagStatus[%flag.team] = "<In the Field>";
%player.unMountImage($FlagSlot);
%flag.hide(false); //Does the throwItem function handle this?
messageTeamExcept(%client, 'MsgCTFFlagDropped', '\c2Teammate %1 dropped the %2 flag.~wfx/misc/flag_drop.wav', %client.name, $teamName[%flag.team], %flag.team);
messageTeam(%flag.team, 'MsgCTFFlagDropped', '\c2Your flag has been dropped by %1!~wfx/misc/flag_drop.wav', %client.name, 0, %flag.team);
messageTeam(0, 'MsgCTFFlagDropped', '\c2%1 dropped the %2 flag.~wfx/misc/flag_drop.wav', %client.name, $teamName[%flag.team], %flag.team);
if(!%player.client.outOfBounds)
messageClient(%client, 'MsgCTFFlagDropped', '\c2You dropped the %2 flag.~wfx/misc/flag_drop.wav', 0, $teamName[%flag.team], %flag.team);
logEcho(%client.nameBase@" (pl "@%player@"/cl "@%client@") dropped team "@%flag.team@" flag");
//don't duplicate the schedule if there's already one in progress...
if ($FlagReturnTimer[%flag] <= 0)
$FlagReturnTimer[%flag] = %game.schedule(%game.FLAG_RETURN_DELAY - %game.fadeTimeMS, "flagReturnFade", %flag);
//call the AI function
%game.AIplayerDroppedFlag(%player, %flag);
}
function CTFGame::flagCap(%game, %player)
{
%client = %player.client;
%flag = %player.holdingFlag;
%flag.carrier = "";
%game.playerLostFlagTarget(%player);
//award points to player and team
messageTeamExcept(%client, 'MsgCTFFlagCapped', '\c2%1 captured the %2 flag!~wfx/misc/flag_capture.wav', %client.name, $teamName[%flag.team], %flag.team, %client.team);
messageTeam(%flag.team, 'MsgCTFFlagCapped', '\c2Your flag was captured by %1.~wfx/misc/flag_lost.wav', %client.name, 0, %flag.team, %client.team);
messageTeam(0, 'MsgCTFFlagCapped', '\c2%1 captured the %2 flag!~wfx/misc/flag_capture.wav', %client.name, $teamName[%flag.team], %flag.team, %client.team);
messageClient(%client, 'MsgCTFFlagCapped', '\c2You captured the %2 flag!~wfx/misc/flag_capture.wav', 0, $teamName[%flag.team], %flag.team, %client.team);
logEcho(%client.nameBase@" (pl "@%player@"/cl "@%client@") capped team "@%client.team@" flag");
%player.holdingFlag = ""; //no longer holding it.
%player.unMountImage($FlagSlot);
%game.flagReset(%flag);
%game.awardScoreFlagCap(%client, %flag);
//call the AI function
%game.AIflagCap(%player, %flag);
//if this cap didn't end the game, play the announcer...
if ($missionRunning)
{
if ($teamName[%client.team] $= 'Inferno')
messageAll("", '~wvoice/announcer/ann.infscores.wav');
else if ($teamName[%client.team] $= 'Storm')
messageAll("", '~wvoice/announcer/ann.stoscores.wav');
}
}
function CTFGame::flagReturnFade(%game, %flag)
{
$FlagReturnTimer[%flag] = %game.schedule(%game.fadeTimeMS, "flagReturn", %flag);
%flag.startFade(%game.fadeTimeMS, 0, true);
}
function CTFGame::flagReturn(%game, %flag, %player)
{
cancel($FlagReturnTimer[%flag]);
$FlagReturnTimer[%flag] = "";
if(%flag.team == 1)
%otherTeam = 2;
else
%otherTeam = 1;
if (%player !$= "")
{
//a player returned it
%client = %player.client;
messageTeamExcept(%client, 'MsgCTFFlagReturned', '\c2Teammate %1 returned your flag to base.~wfx/misc/flag_return.wav', %client.name, 0, %flag.team);
messageTeam(%otherTeam, 'MsgCTFFlagReturned', '\c2Enemy %1 returned the %2 flag.~wfx/misc/flag_return.wav', %client.name, $teamName[%flag.team], %flag.team);
messageTeam(0, 'MsgCTFFlagReturned', '\c2%1 returned the %2 flag.~wfx/misc/flag_return.wav', %client.name, $teamName[%flag.team], %flag.team);
messageClient(%client, 'MsgCTFFlagReturned', '\c2You returned your flag.~wfx/misc/flag_return.wav', 0, $teamName[%flag.team], %flag.team);
logEcho(%client.nameBase@" (pl "@%player@"/cl "@%client@") returned team "@%flag.team@" flag");
%game.awardScoreFlagReturn(%player.client);
}
else
{
//returned due to timer
messageTeam(%otherTeam, 'MsgCTFFlagReturned', '\c2The %2 flag was returned to base.~wfx/misc/flag_return.wav', 0, $teamName[%flag.team], %flag.team); //because it was dropped for too long
messageTeam(%flag.team, 'MsgCTFFlagReturned', '\c2Your flag was returned.~wfx/misc/flag_return.wav', 0, 0, %flag.team);
messageTeam(0, 'MsgCTFFlagReturned', '\c2The %2 flag was returned to base.~wfx/misc/flag_return.wav', 0, $teamName[%flag.team], %flag.team);
logEcho("team "@%flag.team@" flag returned (timeout)");
}
%game.flagReset(%flag);
}
function CTFGame::showStalemateTargets(%game)
{
cancel(%game.stalemateSchedule);
//show the targets
for (%i = 1; %i <= 2; %i++)
{
%flag = $TeamFlag[%i];
//find the object to scope/waypoint....
//render the target hud icon for slot 1 (a centermass flag)
//if we just set him as always sensor vis, it'll work fine.
if (isObject(%flag.carrier))
setTargetAlwaysVisMask(%flag.carrier.getTarget(), 0x7);
}
//schedule the targets to hide
%game.stalemateObjsVisible = true;
%game.stalemateSchedule = %game.schedule(%game.stalemateDurationMS, hideStalemateTargets);
}
function CTFGame::hideStalemateTargets(%game)
{
cancel(%game.stalemateSchedule);
//hide the targets
for (%i = 1; %i <= 2; %i++)
{
%flag = $TeamFlag[%i];
if (isObject(%flag.carrier))
{
%target = %flag.carrier.getTarget();
setTargetAlwaysVisMask(%target, (1 << getTargetSensorGroup(%target)));
}
}
//schedule the targets to show again
%game.stalemateObjsVisible = false;
%game.stalemateSchedule = %game.schedule(%game.stalemateFreqMS, showStalemateTargets);
}
function CTFGame::beginStalemate(%game)
{
%game.stalemate = true;
%game.showStalemateTargets();
}
function CTFGame::endStalemate(%game)
{
%game.stalemate = false;
%game.hideStalemateTargets();
cancel(%game.stalemateSchedule);
}
function CTFGame::flagReset(%game, %flag)
{
//any time a flag is reset, kill the stalemate schedule
%game.endStalemate();
//make sure if there's a player carrying it (probably one out of bounds...), it is stripped first
if (isObject(%flag.carrier))
{
//hide the target hud icon for slot 2 (a centermass flag - visible only as part of a teams sensor network)
%game.playerLostFlagTarget(%flag.carrier);
%flag.carrier.holdingFlag = ""; //no longer holding it.
%flag.carrier.unMountImage($FlagSlot);
}
//fades, restore default position, home, velocity, general status, etc.
%flag.setVelocity("0 0 0");
%flag.setTransform(%flag.originalPosition);
%flag.isHome = true;
%flag.carrier = "";
$flagStatus[%flag.team] = "<At Base>";
%flag.hide(false);
if(%flag.stand)
%flag.stand.getDataBlock().onFlagReturn(%flag.stand);//animate, if exterior stand
//fade the flag in...
%flag.startFade(%game.fadeTimeMS, 0, false);
// dont render base target
setTargetRenderMask(%flag.waypoint.getTarget(), 0);
//call the AI function
%game.AIflagReset(%flag);
}
function CTFGame::timeLimitReached(%game)
{
logEcho("game over (timelimit)");
%game.gameOver();
cycleMissions();
}
function CTFGame::scoreLimitReached(%game)
{
logEcho("game over (scorelimit)");
%game.gameOver();
cycleMissions();
}
function CTFGame::notifyMineDeployed(%game, %mine)
{
//see if the mine is within 5 meters of the flag stand...
%mineTeam = %mine.sourceObject.team;
%homeFlag = $TeamFlag[%mineTeam];
if (isObject(%homeFlag))
{
%dist = VectorDist(%homeFlag.originalPosition, %mine.position);
if (%dist <= %game.notifyMineDist)
{
messageTeam(%mineTeam, 'MsgCTFFlagMined', "The flag has been mined.~wvoice/announcer/flag_minedFem.wav" );
}
}
}
function CTFGame::gameOver(%game)
{
//call the default
DefaultGame::gameOver(%game);
//send the winner message
%winner = "";
if ($teamScore[1] > $teamScore[2])
%winner = $teamName[1];
else if ($teamScore[2] > $teamScore[1])
%winner = $teamName[2];
if (%winner $= 'Storm')
messageAll('MsgGameOver', "Match has ended.~wvoice/announcer/ann.stowins.wav" );
else if (%winner $= 'Inferno')
messageAll('MsgGameOver', "Match has ended.~wvoice/announcer/ann.infwins.wav" );
else
messageAll('MsgGameOver', "Match has ended.~wvoice/announcer/ann.gameover.wav" );
messageAll('MsgClearObjHud', "");
for(%i = 0; %i < ClientGroup.getCount(); %i ++)
{
%client = ClientGroup.getObject(%i);
%game.resetScore(%client);
}
for(%j = 1; %j <= %game.numTeams; %j++)
$TeamScore[%j] = 0;
}
function CTFGame::onClientDamaged(%game, %clVictim, %clAttacker, %damageType, %implement, %damageLoc)
{
//the DefaultGame will set some vars
DefaultGame::onClientDamaged(%game, %clVictim, %clAttacker, %damageType, %implement, %damageLoc);
//if victim is carrying a flag and is not on the attackers team, mark the attacker as a threat for x seconds(for scoring purposes)
if ((%clVictim.holdingFlag !$= "") && (%clVictim.team != %clAttacker.team))
{
%clAttacker.dmgdFlagCarrier = true;
cancel(%clAttacker.threatTimer); //restart timer
%clAttacker.threatTimer = schedule(%game.TIME_CONSIDERED_FLAGCARRIER_THREAT, %clAttacker.dmgdFlagCarrier = false);
}
}
////////////////////////////////////////////////////////////////////////////////////////
function CTFGame::clientMissionDropReady(%game, %client)
{
messageClient(%client, 'MsgClientReady',"", %game.class);
%game.resetScore(%client);
for(%i = 1; %i <= %game.numTeams; %i++)
{
$Teams[%i].score = 0;
messageClient(%client, 'MsgCTFAddTeam', "", %i, $TeamName[%i], $flagStatus[%i], $TeamScore[%i]);
}
//%game.populateTeamRankArray(%client);
//messageClient(%client, 'MsgYourRankIs', "", -1);
messageClient(%client, 'MsgMissionDropInfo', '\c0You are in mission %1 (%2).', $MissionDisplayName, $MissionTypeDisplayName, $ServerName );
DefaultGame::clientMissionDropReady(%game, %client);
}
function CTFGame::assignClientTeam(%game, %client, %respawn)
{
DefaultGame::assignClientTeam(%game, %client, %respawn);
// if player's team is not on top of objective hud, switch lines
messageClient(%client, 'MsgCheckTeamLines', "", %client.team);
}
function CTFGame::recalcScore(%game, %cl)
{
%killValue = %cl.kills * %game.SCORE_PER_KILL;
%deathValue = %cl.deaths * %game.SCORE_PER_DEATH;
if (%killValue - %deathValue == 0)
%killPoints = 0;
else
%killPoints = (%killValue * %killValue) / (%killValue - %deathValue);
%cl.offenseScore = %killPoints +
%cl.suicides * %game.SCORE_PER_SUICIDE + //-1
%cl.escortAssists * %game.SCORE_PER_ESCORT_ASSIST + // 1
%cl.teamKills * %game.SCORE_PER_TEAMKILL + // -1
%cl.flagCaps * %game.SCORE_PER_PLYR_FLAG_CAP + // 3
%cl.genDestroys * %game.SCORE_PER_GEN_DESTROY; // 2
%cl.defenseScore = %cl.genDefends * %game.SCORE_PER_GEN_DEFEND + // 1
%cl.carrierKills * %game.SCORE_PER_CARRIER_KILL + // 1
%cl.escortAssists * %game.SCORE_PER_ESCORT_ASSIST + // 1
%cl.turretKills * %game.SCORE_PER_TURRET_KILL + // 1
%cl.flagReturns * %game.SCORE_PER_FLAG_RETURN + // 1
%cl.genRepairs * %game.SCORE_PER_GEN_REPAIR; // 1
%cl.score = mFloor(%cl.offenseScore + %cl.defenseScore);
%game.recalcTeamRanks(%cl);
}
function CTFGame::updateKillScores(%game, %clVictim, %clKiller, %damageType, %implement)
{
if(%game.testTurretKill(%implement)) //check for turretkill before awarded a non client points for a kill
%game.awardScoreTurretKill(%clVictim, %implement);
else if (%game.testKill(%clVictim, %clKiller)) //verify victim was an enemy
{
%game.awardScoreKill(%clKiller);
%game.awardScoreDeath(%clVictim);
if (%game.testGenDefend(%clVictim, %clKiller))
%game.awardScoreGenDefend(%clKiller);
if(%game.testCarrierKill(%clVictim, %clKiller))
%game.awardScoreCarrierKill(%clKiller);
else
{
if (%game.testFlagDefend(%clVictim, %clKiller))
%game.awardScoreFlagDefend(%clKiller);
}
if (%game.testEscortAssist(%clVictim, %clKiller))
%game.awardScoreEscortAssist(%clKiller);
}
else
{
if (%game.testSuicide(%clVictim, %clKiller, %damageType)) //otherwise test for suicide
{
%game.awardScoreSuicide(%clVictim);
}
else
{
if (%game.testTeamKill(%clVictim, %clKiller)) //otherwise test for a teamkill
%game.awardScoreTeamKill(%clVictim, %clKiller);
}
}
}
function CTFGame::testFlagDefend(%game, %victimID, %killerID)
{
InitContainerRadiusSearch(%victimID.plyrPointOfDeath, %game.RADIUS_FLAG_DEFENSE, $TypeMasks::ItemObjectType);
%objID = containerSearchNext();
while(%objID != 0)
{
%objType = %objID.getDataBlock().getName();
if ((%objType $= "Flag") && (%objID.team == %killerID.team))
return true; //found the(a) killer's flag near the victim's point of death
else
%objID = containerSearchNext();
}
return false; //didn't find a qualifying flag within required radius of victims point of death
}
function CTFGame::testGenDefend(%game, %victimID, %killerID)
{
InitContainerRadiusSearch(%victimID.plyrPointOfDeath, %game.RADIUS_GEN_DEFENSE, $TypeMasks::StaticShapeObjectType);
%objID = containerSearchNext();
while(%objID != 0)
{
%objType = %objID.getDataBlock().ClassName;
if ((%objType $= "generator") && (%objID.team == %killerID.team))
return true; //found a killer's generator within required radius of victim's death
else
%objID = containerSearchNext();
}
return false; //didn't find a qualifying gen within required radius of victim's point of death
}
function CTFGame::testCarrierKill(%game, %victimID, %killerID)
{
%flag = %victimID.plyrDiedHoldingFlag;
return ((%flag !$= "") && (%flag.team == %killerID.team));
}
function CTFGame::testEscortAssist(%game, %victimID, %killerID)
{
return (%victimID.dmgdFlagCarrier);
}
function CTFGame::testGenDestroyed(%game, %obj)
{
return (%obj.lastDamagedByTeam != %obj.team);
}
function CTFGame::testValidRepair(%game, %obj)
{
return ((%obj.lastDamagedByTeam != %obj.team) && (%obj.repairedBy.team == %obj.team));
}
function CTFGame::awardScoreFlagCap(%game, %cl, %flag)
{
%cl.flagCaps++;
$TeamScore[%cl.team] += %game.SCORE_PER_TEAM_FLAG_CAP;
messageAll('MsgTeamScoreIs', "", %cl.team, $TeamScore[%cl.team]);
if (%game.SCORE_PER_TEAM_FLAG_CAP > 0)
{
%plural = (%game.SCORE_PER_PLYR_FLAG_CAP != 1 ? 's' : "");
messageClient(%cl, 'msgCTFFriendCap', '\c0You receive %1 point%2 for capturing the enemy flag!', %game.SCORE_PER_PLYR_FLAG_CAP, %plural);
messageTeamExcept(%cl, 'msgCTFFriendCap', '\c0%1 receives %2 point%3 for capturing the enemy flag!', %cl.name, %game.SCORE_PER_PLYR_FLAG_CAP, %plural);
messageTeam(%flag.team, 'msgCTFEnemyCap', '\c0Enemy %1 received %2 point%3 for capturing your flag!', %cl.name, %game.SCORE_PER_PLYR_FLAG_CAP, %plural);
}
%game.recalcScore(%cl);
%game.checkScoreLimit(%cl.team);
}
function CTFGame::awardScoreFlagTouch(%game, %cl, %flag)
{
%team = %cl.team;
if( $DontScoreTimer[%team] )
return;
$dontScoreTimer[%team] = true;
//tinman - needed to remove all game calls to "eval" for the PURE server...
%game.schedule(%game.TOUCH_DELAY_MS, resetDontScoreTimer, %team);
//schedule(%game.TOUCH_DELAY_MS, 0, eval, "$dontScoreTimer["@%team@"] = false;");
//schedule(%game.TOUCH_DELAY_MS, 0, eval, "$dontScoreTimer["@%team@"] = false;");
$TeamScore[%team] += %game.SCORE_PER_TEAM_FLAG_TOUCH;
messageAll('MsgTeamScoreIs', "", %team, $TeamScore[%team]);
if (%game.SCORE_PER_TEAM_FLAG_TOUCH > 0)
{
%plural = (%game.SCORE_PER_TEAM_FLAG_TOUCH != 1 ? 's' : "");
messageTeam(%team, 'msgCTFFriendFlagTouch', '\c0Your team receives %1 point%2 for grabbing the enemy flag!', %game.SCORE_PER_TEAM_FLAG_TOUCH, %plural);
messageTeam(%flag.team, 'msgCTFEnemyFlagTouch', '\c0Enemy team %1 receives %2 point%3 for grabbing your flag!', %cl.name, %game.SCORE_PER_TEAM_FLAG_TOUCH, %plural);
}
%game.recalcScore(%cl);
%game.checkScoreLimit(%team);
}
function CTFGame::resetDontScoreTimer(%game, %team)
{
$dontScoreTimer[%team] = false;
}
function CTFGame::checkScoreLimit(%game, %team)
{
%scoreLimit = MissionGroup.CTF_scoreLimit * %game.SCORE_PER_TEAM_FLAG_CAP;
// default of 5 if scoreLimit not defined
if(%scoreLimit $= "")
%scoreLimit = 5 * %game.SCORE_PER_TEAM_FLAG_CAP;
if($TeamScore[%team] >= %scoreLimit)
%game.scoreLimitReached();
}
function CTFGame::awardScoreFlagReturn(%game, %cl)
{
%cl.flagReturns++;
if (%game.SCORE_PER_FLAG_RETURN != 0)
{
messageClient(%cl, 'scoreFlaRetMsg', '\c0You received a %1 point bonus for returning your flag.', %game.SCORE_PER_FLAG_RETURN);
messageTeamExcept(%cl, 'scoreFlaRetMsg', '\c0Teammate %1 received a %2 point bonus for returning your flag.', %cl.name, %game.SCORE_PER_FLAG_RETURN);
}
%game.recalcScore(%cl);
}
function CTFGame::awardScoreGenDestroy(%game,%cl)
{
%cl.genDestroys++;
if (%game.SCORE_PER_GEN_DESTROY != 0)
{
messageClient(%cl, 'msgGenDes', '\c0You received a %1 point bonus for destroying an enemy generator.', %game.SCORE_PER_GEN_DESTROY);
messageTeamExcept(%cl, 'msgGenDes', '\c0Teammate %1 received a %2 point bonus for destroying an enemy generator.', %cl.name, %game.SCORE_PER_GEN_DESTROY);
}
%game.recalcScore(%cl);
}
function CTFGame::awardScoreGenRepair(%game, %cl)
{
%cl.genRepairs++;
if (%game.SCORE_PER_GEN_REPAIR != 0)
{
messageClient(%cl, 'msgGenRep', '\c0You received a %1 point bonus for repairing a generator.', %game.SCORE_PER_GEN_REPAIR);
messageTeamExcept(%cl, 'msgGenRep', '\c0Teammate %1 received a %2 point bonus for repairing a generator.', %cl.name, %game.SCORE_PER_GEN_REPAIR);
}
%game.recalcScore(%cl);
}
function CTFGame::awardScoreGenDefend(%game, %killerID)
{
%killerID.genDefends++;
if (%game.SCORE_PER_GEN_DEFEND != 0)
{
messageClient(%killerID, 'msgGenDef', '\c0You received a %1 point bonus for defending a generator.', %game.SCORE_PER_GEN_DEFEND);
messageTeamExcept(%killerID, 'msgGenDef', '\c0Teammate %1 received a %2 point bonus for defending a generator.', %killerID.name, %game.SCORE_PER_GEN_DEFEND);
}
%game.recalcScore(%cl);
}
function CTFGame::awardScoreCarrierKill(%game, %killerID)
{
%killerID.carrierKills++;
if (%game.SCORE_PER_CARRIER_KILL != 0)
{
messageClient(%killerID, 'msgCarKill', '\c0You received a %1 point bonus for stopping the enemy flag carrier!', %game.SCORE_PER_CARRIER_KILL);
messageTeamExcept(%killerID, 'msgCarKill', '\c0Teammate %1 received a %2 point bonus for stopping the enemy flag carrier!', %killerID.name, %game.SCORE_PER_CARRIER_KILL);
}
%game.recalcScore(%killerID);
}
function CTFGame::awardScoreFlagDefend(%game, %killerID)
{
%killerID.flagDefends++;
if (%game.SCORE_PER_FLAG_DEFEND != 0)
{
messageClient(%killerID, 'msgFlagDef', '\c0You received a %1 point bonus for defending your flag!', %game.SCORE_PER_FLAG_DEFEND);
messageTeamExcept(%killerID, 'msgFlagDef', '\c0Teammate %1 received a %2 point bonus for defending your flag!', %killerID.name, %game.SCORE_PER_FLAG_DEFEND);
}
%game.recalcScore(%killerID);
}
function CTFGame::awardScoreEscortAssist(%game, %killerID)
{
%killerID.escortAssists++;
if (%game.SCORE_PER_ESCORT_ASSIST != 0)
{
messageClient(%killerID, 'msgEscAsst', '\c0You received a %1 point bonus for protecting the flag carrier!', %game.SCORE_PER_ESCORT_ASSIST);
messageTeamExcept(%killerID, 'msgEscAsst', '\c0Teammate %1 received a %2 point bonus for protecting the flag carrier!', %killerID.name, %game.SCORE_PER_ESCORT_ASSIST);
}
%game.recalcScore(%killerID);
}
function CTFGame::resetScore(%game, %client)
{
%client.offenseScore = 0;
%client.kills = 0;
%client.deaths = 0;
%client.suicides = 0;
%client.escortAssists = 0;
%client.teamKills = 0;
%client.flagCaps = 0;
%client.genDestroys = 0;
%client.defenseScore = 0;
%client.genDefends = 0;
%client.carrierKills = 0;
%client.escortAssists = 0;
%client.turretKills = 0;
%client.flagReturns = 0;
%client.genRepairs = 0;
%client.score = 0;
}
function CTFGame::genOnRepaired(%game, %obj, %objName)
{
if (%game.testValidRepair(%obj))
{
%repairman = %obj.repairedBy;
messageTeam(%repairman.team, 'msgGenRepaired', '\c0%1 repaired the %2 Generator!', %repairman.name, %obj.nameTag);
%game.awardScoreGenRepair(%obj.repairedBy);
}
}
function CTFGame::stationOnRepaired(%game, %obj, %objName)
{
if (%game.testValidRepair(%obj))
{
%repairman = %obj.repairedBy;
messageTeam(%repairman.team, 'msgStationRepaired', '\c0%1 repaired the %2 Inventory Station!', %repairman.name, %obj.nameTag);
}
}
function CTFGame::sensorOnRepaired(%game, %obj, %objName)
{
if (%game.testValidRepair(%obj))
{
%repairman = %obj.repairedBy;
messageTeam(%repairman.team, 'msgSensorRepaired', '\c0%1 repaired the %2 Pulse Sensor!', %repairman.name, %obj.nameTag);
}
}
function CTFGame::turretOnRepaired(%game, %obj, %objName)
{
if (%game.testValidRepair(%obj))
{
%repairman = %obj.repairedBy;
messageTeam(%repairman.team, 'msgTurretRepaired', '\c0%1 repaired the %2 Turret!', %repairman.name, %obj.nameTag);
}
}
function CTFGame::vStationOnRepaired(%game, %obj, %objName)
{
if (%game.testValidRepair(%obj))
{
%repairman = %obj.repairedBy;
messageTeam(%repairman.team, 'msgTurretRepaired', '\c0%1 repaired the %2 Vehicle Station!', %repairman.name, %obj.nameTag);
}
}
function CTFGame::enterMissionArea(%game, %playerData, %player)
{
if(%player.getState() $= "Dead")
return;
%player.client.outOfBounds = false;
messageClient(%player.client, 'EnterMissionArea', '\c1You are back in the mission area.');
logEcho(%player.client.nameBase@" (pl "@%player@"/cl "@%player.client@") entered mission area");
//the instant a player leaves the mission boundary, the flag is dropped, and the return is scheduled...
if (%player.holdingFlag > 0)
{
cancel($FlagReturnTimer[%player.holdingFlag]);
$FlagReturnTimer[%player.holdingFlag] = "";
}
}
function CTFGame::leaveMissionArea(%game, %playerData, %player)
{
if(%player.getState() $= "Dead")
return;
// maybe we'll do this just in case
%player.client.outOfBounds = true;
// if the player is holding a flag, strip it and throw it back into the mission area
// otherwise, just print a message
if(%player.holdingFlag > 0)
%game.boundaryLoseFlag(%player);
else
messageClient(%player.client, 'MsgLeaveMissionArea', '\c1You have left the mission area.~wfx/misc/warning_beep.wav');
logEcho(%player.client.nameBase@" (pl "@%player@"/cl "@%player.client@") left mission area");
}
function CTFGame::boundaryLoseFlag(%game, %player)
{
// this is called when a player goes out of the mission area while holding
// the enemy flag. - make sure the player is still out of bounds
if (!%player.client.outOfBounds || !isObject(%player.holdingFlag))
return;
%client = %player.client;
%flag = %player.holdingFlag;
%flag.setVelocity("0 0 0");
%flag.setTransform(%player.getWorldBoxCenter());
%flag.setCollisionTimeout(%player);
%game.playerDroppedFlag(%player);
// now for the tricky part -- throwing the flag back into the mission area
// let's try throwing it back towards its "home"
%home = %flag.originalPosition;
%vecx = firstWord(%home) - firstWord(%player.getWorldBoxCenter());
%vecy = getWord(%home, 1) - getWord(%player.getWorldBoxCenter(), 1);
%vecz = getWord(%home, 2) - getWord(%player.getWorldBoxCenter(), 2);
%vec = %vecx SPC %vecy SPC %vecz;
// normalize the vector, scale it, and add an extra "upwards" component
%vecNorm = VectorNormalize(%vec);
%vec = VectorScale(%vecNorm, 1500);
%vec = vectorAdd(%vec, "0 0 500");
// apply the impulse to the flag object
%flag.applyImpulse(%player.getWorldBoxCenter(), %vec);
//don't forget to send the message
messageClient(%player.client, 'MsgCTFFlagDropped', '\c1You have left the mission area and lost the flag.~wfx/misc/flag_drop.wav', 0, 0, %player.holdingFlag.team);
logEcho(%player.client.nameBase@" (pl "@%player@"/cl "@%player.client@") lost flag (out of bounds)");
}
function CTFGame::dropFlag(%game, %player)
{
if(%player.holdingFlag > 0)
{
if (!%player.client.outOfBounds)
%player.throwObject(%player.holdingFlag);
else
%game.boundaryLoseFlag(%player);
}
}
function CTFGame::applyConcussion(%game, %player)
{
%game.dropFlag( %player );
}
function CTFGame::vehicleDestroyed(%game, %vehicle, %destroyer)
{
//vehicle name
%data = %vehicle.getDataBlock();
//%vehicleType = getTaggedString(%data.targetNameTag) SPC getTaggedString(%data.targetTypeTag);
%vehicleType = getTaggedString(%data.targetTypeTag);
if(%vehicleType !$= "MPB")
%vehicleType = strlwr(%vehicleType);
%pref = (%vehicleType $= "Assault Tank") ? "an" : "a";
%enemyTeam = ( %destroyer.team == 1 ) ? 2 : 1;
//what destroyed this vehicle
if(%destroyer.client)
{
//it was a player, or his mine, satchel, whatever...
%destroyer = %destroyer.client;
}
else if(%destroyer.getClassName() $= "Turret")
{
if(%destroyer.getControllingClient())
{
//manned turret
%destroyer = %destroyer.getControllingClient();
}
else
{
%destroyerName = "A turret";
}
}
else if(%destroyer.getDataBlock().catagory $= "Vehicles")
{
// Vehicle vs vehicle kill!
if(%name $= "BomberFlyer" || %name $= "AssaultVehicle")
%gunnerNode = 1;
else
%gunnerNode = 0;
if(%destroyer.getMountNodeObject(%gunnerNode))
%destroyer = %destroyer.getMountNodeObject(gunnerNode).client;
}
else // Is there anything else we care about?
return;
if(%destroyerName $= "")
%destroyerName = %destroyer.name;
if(%vehicle.team == %destroyer.team) // team kill
messageAll( 'msgVehicleTeamDestroy', '\c0%1 TEAMKILLED %3 %2!', %destroyerName, %vehicleType, %pref);
else // legit kill
{
messageTeamExcept(%destroyer, 'msgVehicleDestroy', '\c0%1 destroyed an enemy %2.', %destroyerName, %vehicleType);
messageTeam(%enemyTeam, 'msgVehicleDestroy', '\c0%1 destroyed your team\'s %2.', %destroyerName, %vehicleType);
messageClient(%destroyer, 'msgVehicleDestroy', '\c0You destroyed an enemy %1.', %vehicleType);
}
}