commit 0c4253f7436a71ece57237a5b52664ab72042f51 Author: ChocoTaco Date: Thu Jun 28 14:34:52 2018 -0400 Initial commit diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..dfe0770 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/CTFGame.cs b/CTFGame.cs new file mode 100644 index 0000000..e851fd0 --- /dev/null +++ b/CTFGame.cs @@ -0,0 +1,1949 @@ +// 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"); + +//-- tracking --- +function CTFGame::initGameVars(%game) +{ + // --------------------------------------------------- + // z0dd - ZOD, 9/29/02. Removed T2 demo code from here + %game.SCORE_PER_SUICIDE = 0; // z0dd - ZOD, 8/19/02. No penalty for suicide! Was -10 + %game.SCORE_PER_TEAMKILL = -10; + %game.SCORE_PER_DEATH = 0; + %game.SCORE_PER_TK_DESTROY = -10; // z0dd - ZOD, 10/03/02. Penalty for TKing equiptment. + + %game.SCORE_PER_KILL = 10; + %game.SCORE_PER_PLYR_FLAG_CAP = 30; + %game.SCORE_PER_PLYR_FLAG_TOUCH = 20; + %game.SCORE_PER_TEAM_FLAG_CAP = 100; + %game.SCORE_PER_TEAM_FLAG_TOUCH = 1; + %game.SCORE_PER_ESCORT_ASSIST = 5; + %game.SCORE_PER_HEADSHOT = 1; + %game.SCORE_PER_REARSHOT = 1; // z0dd - ZOD, 8/25/02. Added Lance rear shot messages + + %game.SCORE_PER_TURRET_KILL = 10; // controlled + %game.SCORE_PER_TURRET_KILL_AUTO = 5; // uncontrolled, z0dd - ZOD, 5/27/03. More points, was 3 + %game.SCORE_PER_FLAG_DEFEND = 5; + %game.SCORE_PER_CARRIER_KILL = 5; + %game.SCORE_PER_FLAG_RETURN = 10; + %game.SCORE_PER_STALEMATE_RETURN = 15; + %game.SCORE_PER_GEN_DEFEND = 5; + + %game.SCORE_PER_DESTROY_GEN = 10; + %game.SCORE_PER_DESTROY_SENSOR = 4; + %game.SCORE_PER_DESTROY_TURRET = 5; + %game.SCORE_PER_DESTROY_ISTATION = 2; + %game.SCORE_PER_DESTROY_VSTATION = 5; + %game.SCORE_PER_DESTROY_MPBTSTATION = 3; // z0dd - ZOD, 4/24/02. MPB Teleporter + %game.SCORE_PER_DESTROY_SOLAR = 5; + %game.SCORE_PER_DESTROY_SENTRY = 4; + %game.SCORE_PER_DESTROY_DEP_SENSOR = 1; + %game.SCORE_PER_DESTROY_DEP_INV = 2; + %game.SCORE_PER_DESTROY_DEP_TUR = 3; + + %game.SCORE_PER_DESTROY_SHRIKE = 5; + %game.SCORE_PER_DESTROY_BOMBER = 8; + %game.SCORE_PER_DESTROY_TRANSPORT = 5; + %game.SCORE_PER_DESTROY_WILDCAT = 5; + %game.SCORE_PER_DESTROY_TANK = 8; + %game.SCORE_PER_DESTROY_MPB = 12; + %game.SCORE_PER_PASSENGER = 2; + + %game.SCORE_PER_REPAIR_GEN = 8; + %game.SCORE_PER_REPAIR_SENSOR = 1; + %game.SCORE_PER_REPAIR_TURRET = 4; + %game.SCORE_PER_REPAIR_ISTATION = 2; + %game.SCORE_PER_REPAIR_VSTATION = 4; + %game.SCORE_PER_REPAIR_MPBTSTATION = 3; // z0dd - ZOD, 4/24/02. MPB Teleporter + %game.SCORE_PER_REPAIR_SOLAR = 4; + %game.SCORE_PER_REPAIR_SENTRY = 2; + %game.SCORE_PER_REPAIR_DEP_SEN = 1; // z0dd - ZOD, 4/24/02. Deployed sensors + %game.SCORE_PER_REPAIR_DEP_TUR = 3; + %game.SCORE_PER_REPAIR_DEP_INV = 2; + + %game.FLAG_RETURN_DELAY = 45 * 1000; + + %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; + $stalemate = false; + %game.stalemateObjsVisible = false; + %game.stalemateTimeMS = 60000; + %game.stalemateFreqMS = 15000; + %game.stalemateDurationMS = 6000; + + // --------------------------------------------------- + + // z0dd - ZOD, 9/29/02. Removed T2 demo code from here +} + +package CTFGame +{ + function ShapeBaseData::onDestroyed(%data, %obj, %prevstate) + { + // z0dd - ZOD, 5/27/03. Total re-write + %scorer = %obj.lastDamagedBy; + if(!isObject(%scorer)) + return; + + if((%scorer.getType() & $TypeMasks::GameBaseObjectType) && %scorer.getDataBlock().catagory $= "Vehicles") + { + %name = %scorer.getDatablock().getName(); + if(%name $= "BomberFlyer" || %name $= "AssaultVehicle") + %gunnerNode = 1; + else + %gunnerNode = 0; + + if(%scorer.getMountNodeObject(%gunnerNode)) + { + %destroyer = %scorer.getMountNodeObject(%gunnerNode).client; + %scorer = %destroyer; + %damagingTeam = %scorer.team; + } + } + else if(%scorer.getClassName() $= "Turret") + { + if(%scorer.getControllingClient()) + { + // manned turret + %destroyer = %scorer.getControllingClient(); + %scorer = %destroyer; + %damagingTeam = %scorer.team; + } + else + %scorer = %scorer.owner; // unmanned turret + } + if(!%damagingTeam) + %damagingTeam = %scorer.team; + + if(%damagingTeam != %obj.team) + { + if(!%obj.soiledByEnemyRepair) + Game.awardScoreStaticShapeDestroy(%scorer, %obj); + } + else + { + if(!%obj.getDataBlock().deployedObject) + Game.awardScoreTkDestroy(%scorer, %obj); + + return; + } + } + +function ShapeBaseData::onDisabled(%data, %obj) +{ + %obj.wasDisabled = true; + Parent::onDisabled(%data, %obj); +} + +function RepairGunImage::onRepair(%this, %obj, %slot) +{ + Parent::onRepair(%this, %obj, %slot); + %target = %obj.repairing; + if(%target && %target.team != %obj.team) + { + //error("Enemy stuff("@%obj@") is being repaired (by "@%target@")"); + %target.soiledByEnemyRepair = true; + } +} + +function Flag::objectiveInit(%data, %flag) +{ + if (!%flag.isTeamSkinned) + { + %pos = %flag.getTransform(); + %group = %flag.getGroup(); + } + %flag.originalPosition = %flag.getTransform(); + $flagPos[%flag.team] = %flag.originalPosition; + %flag.isHome = true; + %flag.carrier = ""; + %flag.grabber = ""; + setTargetSkin(%flag.getTarget(), CTFGame::getTeamSkin(CTFGame, %flag.team)); + setTargetSensorGroup(%flag.getTarget(), %flag.team); + setTargetAlwaysVisMask(%flag.getTarget(), 0x7); + setTargetRenderMask(%flag.getTarget(), getTargetRenderMask(%flag.getTarget()) | 0x2); + %flag.scopeWhenSensorVisible(true); + $flagStatus[%flag.team] = ""; + + //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); + //--------------------------------------------------------------------------------------------------------------------------- + // z0dd - ZOD, 3/16/02. Added TSStatic, console spam fix. + if(%this.getClassName() !$= "InteriorInstance" && %this.getClassName() !$= "SimGroup" && %this.getClassName() !$= "TSStatic") + { + if(%this.getDataBlock().getName() $= "ExteriorFlagStand") + { + %flag.stand = %this; + %this.flag = %flag; + } + } + } + + // set the nametag on the target + setTargetName(%flag.getTarget(), CTFGame::getTeamName(CTFGame, %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, CTFGame::getTeamName( CTFGame, %flag.team), "", "", 'Base', %flag.team, 0); + setTargetAlwaysVisMask(%target, 0xffffffff); + + //store the flag in an array + $TeamFlag[%flag.team] = %flag; + + // -------------------------------------------------------- + // z0dd - ZOD, 5/26/02. Don't let flag hover over defenders + %flag.static = true; + + // ------------------------------------------------------------------------------------------- + // z0dd - ZOD, 10/03/02. Use triggers for flags that are at home, hack for flag collision bug. + %flag.trigger = new Trigger() + { + dataBlock = flagTrigger; + polyhedron = "-0.6 0.6 0.1 1.2 0.0 0.0 0.0 -1.2 0.0 0.0 0.0 2.5"; + position = %flag.position; + rotation = %flag.rotation; + }; + MissionCleanup.add(%flag.trigger); + %flag.trigger.flag = %flag; + // ------------------------------------------------------------------------------------------- +} + +function Flag::onEnterLiquid(%data, %obj, %coverage, %type) +{ + if(%type > 3) // 1-3 are water, 4+ is lava and quicksand(?) + { + //error("flag("@%obj@") is in liquid type" SPC %type); + // Changed slightly so this can be cancelled if it leaves the + // lava before its supposed to be returned - Ilys + %obj.lavaEnterThread = Game.schedule(3000, "flagReturn", %obj); + } +} + +function Flag::onLeaveLiquid(%data, %obj, %type) +{ + // Added to stop the flag retrun if it slides out of the lava - Ilys + if(isEventPending(%obj.lavaEnterThread)) + cancel(%obj.lavaEnterThread); +} +}; + +//-------------------------------------------------------------------------- +// 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::getTeamSkin(%game, %team) +{ + if($host::tournamentMode) // z0dd - ZOD, 9/29/02. Removed T2 demo code from here + { + return $teamSkin[%team]; + } + + else + { + //error("CTFGame::getTeamSkin"); + if(!$host::useCustomSkins) + { + %terrain = MissionGroup.musicTrack; + //error("Terrain type is: " SPC %terrain); + switch$(%terrain) + { + case "lush": + if(%team == 1) + %skin = 'beagle'; + else if(%team == 2) + %skin = 'dsword'; + else %skin = 'base'; + + case "badlands": + if(%team == 1) + %skin = 'swolf'; + else if(%team == 2) + %skin = 'dsword'; + else %skin = 'base'; + + case "ice": + if(%team == 1) + %skin = 'swolf'; + else if(%team == 2) + %skin = 'beagle'; + else %skin = 'base'; + + case "desert": + if(%team == 1) + %skin = 'cotp'; + else if(%team == 2) + %skin = 'beagle'; + else %skin = 'base'; + + case "Volcanic": + if(%team == 1) + %skin = 'dsword'; + else if(%team == 2) + %skin = 'cotp'; + else %skin = 'base'; + + default: + if(%team == 2) + %skin = 'baseb'; + else %skin = 'base'; + } + } + else %skin = $teamSkin[%team]; + + //error("%skin = " SPC getTaggedString(%skin)); + return %skin; +} +} + +function CTFGame::getTeamName(%game, %team) +{ + // --------------------------------------------------- + // z0dd - ZOD 3/30/02. Only display default team names + //if ( isDemo() || $host::tournamentMode) + return $TeamName[%team]; + // --------------------------------------------------- +} + +//-------------------------------------------------------------------------- +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; + + echo( "starting camp thread..." ); + %game.campThread_1 = schedule( 1000, 0, "checkVehicleCamping", 1 ); + %game.campThread_2 = schedule( 1000, 0, "checkVehicleCamping", 2 ); +} + +function CTFGame::playerTouchFlag(%game, %player, %flag) +{ + %client = %player.client; + if ((%flag.carrier $= "") && (%player.getState() !$= "Dead")) + { + // z0dd - ZOD, 5/07/04. Cancel the lava return. + if(isEventPending(%obj.lavaEnterThread)) + cancel(%obj.lavaEnterThread); + + //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) +{ + // --------------------------------------------------------------- + // z0dd, ZOD - 9/27/02. Player must wait to grab after throwing it + if((%player.flagTossWait !$= "") && %player.flagTossWait) + return false; + // --------------------------------------------------------------- + + cancel(%flag.searchSchedule); // z0dd - ZOD, 9/28/02. Hack for flag collision bug. SquirrelOfDeath, 10/02/02: Moved from PlayerTouchFlag + + cancel(%game.updateFlagThread[%flag]); // z0dd - ZOD, 8/4/02. Cancel this flag's thread to KineticPoet's flag updater + %game.flagHeldTime[%flag] = getSimTime(); // z0dd - ZOD, 8/15/02. Store time player grabbed 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, %game.getTeamSkin(%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; + %teamName = %game.getTeamName(%flag.team); + messageTeamExcept(%client, 'MsgCTFFlagTaken', '\c2Teammate %1 took the %2 flag.~wfx/misc/flag_snatch.wav', %client.name, %teamName, %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, %client.nameBase); + messageClient(%client, 'MsgCTFFlagTaken', '\c2You took the %2 flag.~wfx/misc/flag_snatch.wav', %client.name, %teamName, %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))); +} + +//---------------------------------------------------------------------------------------- +// z0dd - ZOD, 8/4/02: KineticPoet's flag updater code +function CTFGame::updateFlagTransform(%game, %flag) +{ + %flag.setTransform(%flag.getTransform()); + %game.updateFlagThread[%flag] = %game.schedule(100, "updateFlagTransform", %flag); +} +//---------------------------------------------------------------------------------------- + +function CTFGame::playerDroppedFlag(%game, %player) +{ + %client = %player.client; + %flag = %player.holdingFlag; + %game.updateFlagTransform(%flag); // z0dd - ZOD, 8/4/02, Call to KineticPoet's flag updater + %held = %game.formatTime(getSimTime() - %game.flagHeldTime[%flag], false); // z0dd - ZOD, 8/15/02. How long did player hold flag? + + %game.playerLostFlagTarget(%player); + + %player.holdingFlag = ""; //player isn't holding a flag anymore + %flag.carrier = ""; //flag isn't held anymore + $flagStatus[%flag.team] = ""; + + %player.unMountImage($FlagSlot); + %flag.hide(false); //Does the throwItem function handle this? + + %teamName = %game.getTeamName(%flag.team); + messageTeamExcept(%client, 'MsgCTFFlagDropped', '\c2Teammate %1 dropped the %2 flag. (Held: %4)~wfx/misc/flag_drop.wav', %client.name, %teamName, %flag.team, %held); // z0dd - ZOD, 8/15/02. How long flag was held + messageTeam(%flag.team, 'MsgCTFFlagDropped', '\c2Your flag has been dropped by %1! (Held: %4)~wfx/misc/flag_drop.wav', %client.name, 0, %flag.team, %held); // z0dd - ZOD, 8/15/02. How long flag was held + messageTeam(0, 'MsgCTFFlagDropped', '\c2%1 dropped the %2 flag. (Held: %4)~wfx/misc/flag_drop.wav', %client.name, %teamName, %flag.team, %held); // z0dd - ZOD, 8/15/02. How long flag was held + if(!%player.client.outOfBounds) + messageClient(%client, 'MsgCTFFlagDropped', '\c2You dropped the %2 flag. (Held: %4)~wfx/misc/flag_drop.wav', %client.name, %teamName, %flag.team, %held); // z0dd - ZOD, 8/15/02. How long flag was held + // Yogi, 8/18/02. 3rd param changed 0 -> %client.name + logEcho(%client.nameBase@" (pl "@%player@"/cl "@%client@") dropped team "@%flag.team@" flag"@" (Held: "@%held@")"); + + //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 = ""; + + %held = %game.formatTime(getSimTime() - %game.flagHeldTime[%flag], false); // z0dd - ZOD, 8/15/02. How long did player hold flag? + + %game.playerLostFlagTarget(%player); + //award points to player and team + %teamName = %game.getTeamName(%flag.team); + messageTeamExcept(%client, 'MsgCTFFlagCapped', '\c2%1 captured the %2 flag! (Held: %5)~wfx/misc/flag_capture.wav', %client.name, %teamName, %flag.team, %client.team, %held); + messageTeam(%flag.team, 'MsgCTFFlagCapped', '\c2Your flag was captured by %1. (Held: %5)~wfx/misc/flag_lost.wav', %client.name, 0, %flag.team, %client.team, %held); + messageTeam(0, 'MsgCTFFlagCapped', '\c2%1 captured the %2 flag! (Held: %5)~wfx/misc/flag_capture.wav', %client.name, %teamName, %flag.team, %client.team, %held); + messageClient(%client, 'MsgCTFFlagCapped', '\c2You captured the %2 flag! (Held: %5)~wfx/misc/flag_capture.wav', %client.name, %teamName, %flag.team, %client.team, %held); // Yogi, 8/18/02. 3rd param changed 0 -> %client.name + + logEcho(%client.nameBase@" (pl "@%player@"/cl "@%client@") capped team "@%client.team@" flag"@" (Held: "@%held@")"); + %player.holdingFlag = ""; //no longer holding it. + %player.unMountImage($FlagSlot); + %game.awardScoreFlagCap(%client, %flag); + %game.flagReset(%flag); + + //call the AI function + %game.AIflagCap(%player, %flag); + + //if this cap didn't end the game, play the announcer... + if ($missionRunning) + { + if (%game.getTeamName(%client.team) $= 'Inferno') + messageAll("", '~wvoice/announcer/ann.infscores.wav'); + else if (%game.getTeamName(%client.team) $= 'Storm') + messageAll("", '~wvoice/announcer/ann.stoscores.wav'); + else if (%game.getTeamName(%client.team) $= 'Phoenix') + messageAll("", '~wvoice/announcer/ann.pxscore.wav'); + else if (%game.getTeamName(%client.team) $= 'Blood Eagle') + messageAll("", '~wvoice/announcer/ann.bescore.wav'); + else if (%game.getTeamName(%client.team) $= 'Diamond Sword') + messageAll("", '~wvoice/announcer/ann.dsscore.wav'); + else if (%game.getTeamName(%client.team) $= 'Starwolf') + messageAll("", '~wvoice/announcer/ann.swscore.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) +{ + // Kill the lava scedule - Ilys + if(isEventPending(%flag.lavaEnterThread)) + cancel(%flag.lavaEnterThread); + + cancel($FlagReturnTimer[%flag]); + $FlagReturnTimer[%flag] = ""; + + if(%flag.team == 1) + %otherTeam = 2; + else + %otherTeam = 1; + %teamName = %game.getTeamName(%flag.team); + 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); + messageTeam(0, 'MsgCTFFlagReturned', '\c2%1 returned the %2 flag.~wfx/misc/flag_return.wav', %client.name, %teamName, %flag.team); + messageClient(%client, 'MsgCTFFlagReturned', '\c2You returned your flag.~wfx/misc/flag_return.wav', %client.name, %teamName, %flag.team); // Yogi, 8/18/02. 3rd param changed 0 -> %client.name + logEcho(%client.nameBase@" (pl "@%player@"/cl "@%client@") returned team "@%flag.team@" flag"); + + // find out what type of return it is + // stalemate return? + + // --------------------------------------------------- + // z0dd - ZOD, 9/29/02. Removed T2 demo code from here + if(%game.stalemate) + { + //error("Stalemate return!!!"); + %game.awardScoreStalemateReturn(%player.client); + } + // regular return + else + { + %enemyFlagDist = vectorDist($flagPos[%flag.team], $flagPos[%otherTeam]); + %dist = vectorDist(%flag.position, %flag.originalPosition); + + %rawRatio = %dist/%enemyFlagDist; + %ratio = %rawRatio < 1 ? %rawRatio : 1; + %percentage = mFloor( (%ratio) * 10 ) * 10; + %game.awardScoreFlagReturn(%player.client, %percentage); + } + // --------------------------------------------------- + } + else + { + //returned due to timer + messageTeam(%otherTeam, 'MsgCTFFlagReturned', '\c2The %2 flag was returned to base.~wfx/misc/flag_return.wav', 0, %teamName, %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); + 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(); + + // z0dd - ZOD, 5/27/03. Added anti-turtling, return flags after x minutes + if(!$Host::TournamentMode) + { + messageAll( 'MsgStalemate', '\c3Anti turtle initialized. Flags will be returned to bases in %1 minutes.', $Host::ClassicAntiTurtleTime); + %game.turtleSchedule = %game.schedule($Host::ClassicAntiTurtleTime * 60000, 'antiTurtle'); + } +} + +function CTFGame::endStalemate(%game) +{ + %game.stalemate = false; + %game.hideStalemateTargets(); + cancel(%game.stalemateSchedule); +} + +// z0dd - ZOD, 5/27/03. Anti-turtle function +function CTFGame::antiTurtle(%game) +{ + if(isEventPending(%game.turtleSchedule)) + cancel(%game.turtleSchedule); + + for (%i = 1; %i <= 2; %i++) + { + Game.flagReturn($TeamFlag[%i]); + messageAll( 'MsgCTFFlagReturned', '\c3Both flags returned to bases to break stalemate.~wfx/misc/flag_return.wav', 0, 0, %i); + } +} + +function CTFGame::flagReset(%game, %flag) +{ + cancel(%game.updateFlagThread[%flag]); // z0dd - ZOD, 8/4/02. Cancel this flag's thread to KineticPoet's flag updater + + //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 = ""; + %flag.grabber = ""; + $flagStatus[%flag.team] = ""; + %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); + + // -------------------------------------------------------- + // z0dd - ZOD, 5/26/02. Don't let flag hover over defenders + %flag.static = true; + + // -------------------------------------------------------------------------- + // z0dd - ZOD, 9/28/02. Hack for flag collision bug. + if(%flag.searchSchedule !$="") + { + cancel(%flag.searchSchedule); + } + // -------------------------------------------------------------------------- +} + +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) +{ + // z0dd - ZOD, 5/27/03. Kill the anti-turtle schedule + if(%game.turtleSchedule !$= "") + { + cancel(%game.turtleSchedule); + %game.turtleSchedule = ""; + } + + // z0dd - ZOD, 9/28/02. Hack for flag collision bug. + for(%f = 1; %f <= %game.numTeams; %f++) + { + cancel($TeamFlag[%f].searchSchedule); + cancel($TeamFlag[%f].lavaEnterThread); // Kill Ilys lava schedule - ZOD + } + + // ------------------------------------------- + // z0dd - ZOD, 9/28/02. Cancel camp schedules. + if( Game.campThread_1 !$= "" ) + cancel(Game.campThread_1); + + if( Game.campThread_2 !$= "" ) + cancel(Game.campThread_2); + // ------------------------------------------- + + //call the default + DefaultGame::gameOver(%game); + + //send the winner message + %winner = ""; + if ($teamScore[1] > $teamScore[2]) + %winner = %game.getTeamName(1); + else if ($teamScore[2] > $teamScore[1]) + %winner = %game.getTeamName(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 if (%winner $= 'Starwolf') + messageAll('MsgGameOver', "Match has ended.~wvoice/announcer/ann.swwin.wav" ); + else if (%winner $= 'Blood Eagle') + messageAll('MsgGameOver', "Match has ended.~wvoice/announcer/ann.bewin.wav" ); + else if (%winner $= 'Diamond Sword') + messageAll('MsgGameOver', "Match has ended.~wvoice/announcer/ann.dswin.wav" ); + else if (%winner $= 'Phoenix') + messageAll('MsgGameOver', "Match has ended.~wvoice/announcer/ann.pxwin.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) +{ + if(%clVictim.headshot && %damageType == $DamageType::Laser && %clVictim.team != %clAttacker.team) + { + %clAttacker.scoreHeadshot++; + if (%game.SCORE_PER_HEADSHOT != 0) + { + messageClient(%clAttacker, 'msgHeadshot', '\c0You received a %1 point bonus for a successful headshot.', %game.SCORE_PER_HEADSHOT); + messageTeamExcept(%clAttacker, 'msgHeadshot', '\c5%1 hit a sniper rifle headshot.', %clAttacker.name); // z0dd - ZOD, 8/15/02. Tell team + } + %game.recalcScore(%clAttacker); + } + + // ----------------------------------------------- + // z0dd - ZOD, 8/25/02. Rear Lance hits + if(%clVictim.rearshot && %damageType == $DamageType::ShockLance && %clVictim.team != %clAttacker.team) + { + %clAttacker.scoreRearshot++; + if (%game.SCORE_PER_REARSHOT != 0) + { + messageClient(%clAttacker, 'msgRearshot', '\c0You received a %1 point bonus for a successful rearshot.', %game.SCORE_PER_REARSHOT); + messageTeamExcept(%clAttacker, 'msgRearshot', '\c5%1 hit a shocklance rearshot.', %clAttacker.name); + } + %game.recalcScore(%clAttacker); + } + // ----------------------------------------------- + + //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, %game.getTeamName(%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); + + // --------------------------------------------------- + // z0dd - ZOD, 9/29/02. Removed T2 demo code from here + %cl.offenseScore = %killPoints + + %cl.suicides * %game.SCORE_PER_SUICIDE + + %cl.escortAssists * %game.SCORE_PER_ESCORT_ASSIST + + %cl.teamKills * %game.SCORE_PER_TEAMKILL + + %cl.tkDestroys * %game.SCORE_PER_TK_DESTROY + // z0dd - ZOD, 10/03/02. Penalty for tking equiptment. + %cl.scoreHeadshot * %game.SCORE_PER_HEADSHOT + + %cl.scoreRearshot * %game.SCORE_PER_REARSHOT + // z0dd - ZOD, 8/25/02. Added Lance rear shot messages + %cl.flagCaps * %game.SCORE_PER_PLYR_FLAG_CAP + + %cl.flagGrabs * %game.SCORE_PER_PLYR_FLAG_TOUCH + + %cl.genDestroys * %game.SCORE_PER_DESTROY_GEN + + %cl.sensorDestroys * %game.SCORE_PER_DESTROY_SENSOR + + %cl.turretDestroys * %game.SCORE_PER_DESTROY_TURRET + + %cl.iStationDestroys * %game.SCORE_PER_DESTROY_ISTATION + + %cl.vstationDestroys * %game.SCORE_PER_DESTROY_VSTATION + + %cl.mpbtstationDestroys * %game.SCORE_PER_DESTROY_MPBTSTATION + // z0dd - ZOD 3/30/02. MPB Teleporter + %cl.solarDestroys * %game.SCORE_PER_DESTROY_SOLAR + + %cl.sentryDestroys * %game.SCORE_PER_DESTROY_SENTRY + + %cl.depSensorDestroys * %game.SCORE_PER_DESTROY_DEP_SENSOR + + %cl.depTurretDestroys * %game.SCORE_PER_DESTROY_DEP_TUR + + %cl.depStationDestroys * %game.SCORE_PER_DESTROY_DEP_INV + + %cl.vehicleScore + %cl.vehicleBonus; + + %cl.defenseScore = %cl.genDefends * %game.SCORE_PER_GEN_DEFEND + + %cl.flagDefends * %game.SCORE_PER_FLAG_DEFEND + + %cl.carrierKills * %game.SCORE_PER_CARRIER_KILL + + %cl.escortAssists * %game.SCORE_PER_ESCORT_ASSIST + + %cl.turretKills * %game.SCORE_PER_TURRET_KILL_AUTO + + %cl.mannedturretKills * %game.SCORE_PER_TURRET_KILL + + %cl.genRepairs * %game.SCORE_PER_REPAIR_GEN + + %cl.SensorRepairs * %game.SCORE_PER_REPAIR_SENSOR + + %cl.TurretRepairs * %game.SCORE_PER_REPAIR_TURRET + + %cl.StationRepairs * %game.SCORE_PER_REPAIR_ISTATION + + %cl.VStationRepairs * %game.SCORE_PER_REPAIR_VSTATION + + %cl.mpbtstationRepairs * %game.SCORE_PER_REPAIR_MPBTSTATION + // z0dd - ZOD 4/10/04. MPB Teleporter + %cl.depSensorRepairs * %game.SCORE_PER_REPAIR_DEP_SEN + // z0dd - ZOD 5/27/03. Deployed sensors %cl.mpbtstationRepairs * %game.SCORE_PER_REPAIR_MPBTSTATION + // z0dd - ZOD 3/30/02. MPB Teleporter + %cl.solarRepairs * %game.SCORE_PER_REPAIR_SOLAR + + %cl.sentryRepairs * %game.SCORE_PER_REPAIR_SENTRY + + %cl.depInvRepairs * %game.SCORE_PER_REPAIR_DEP_INV + + %cl.depTurretRepairs * %game.SCORE_PER_REPAIR_DEP_TUR + + %cl.returnPts; + // --------------------------------------------------- + + // z0dd - ZOD, 9/29/02. Removed T2 demo code from here + + %cl.score = mFloor(%cl.offenseScore + %cl.defenseScore); + + %game.recalcTeamRanks(%cl); +} + +function CTFGame::updateKillScores(%game, %clVictim, %clKiller, %damageType, %implement) +{ +// is this a vehicle kill rather than a player kill + + // console error message suppression + if( isObject( %implement ) ) + { + if( %implement.getDataBlock().getName() $= "AssaultPlasmaTurret" || %implement.getDataBlock().getName() $= "BomberTurret" ) // gunner + %clKiller = %implement.vehicleMounted.getMountNodeObject(1).client; + else if(%implement.getDataBlock().catagory $= "Vehicles") // pilot + %clKiller = %implement.getMountNodeObject(0).client; + } + + 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 + { + %value = %game.awardScoreKill(%clKiller); + %game.shareScore(%clKiller, %value); + %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::awardScoreFlagCap(%game, %cl, %flag) +{ + %cl.flagCaps++; + $TeamScore[%cl.team] += %game.SCORE_PER_TEAM_FLAG_CAP; + messageAll('MsgTeamScoreIs', "", %cl.team, $TeamScore[%cl.team]); + + %flag.grabber.flagGrabs++; + + if (%game.SCORE_PER_TEAM_FLAG_CAP > 0) + { + %plural = (%game.SCORE_PER_PLYR_FLAG_CAP != 1 ? 's' : ""); + %plural2 = (%game.SCORE_PER_PLYR_FLAG_TOUCH != 1 ? 's' : ""); + + // z0dd - ZOD, 9/29/02. Removed T2 demo code from here + + // z0dd - ZOD, 9/29/02. Removed T2 demo code from here + if(%cl == %flag.grabber) + { + messageClient(%cl, 'msgCTFFriendCap', '\c0You receive %1 point%2 for stealing and capturing the enemy flag!', %game.SCORE_PER_PLYR_FLAG_CAP+%game.SCORE_PER_PLYR_FLAG_TOUCH, %plural); + messageTeam(%flag.team, 'msgCTFEnemyCap', '\c0Enemy %1 received %2 point%3 for capturing your flag!', %cl.name, %game.SCORE_PER_PLYR_FLAG_CAP+%game.SCORE_PER_PLYR_FLAG_TOUCH, %plural); + //messageTeamExcept(%cl, 'msgCTFFriendCap', '\c0Teammate %1 receives %2 point%3 for capturing the enemy flag!', %cl.name, %game.SCORE_PER_PLYR_FLAG_CAP+%game.SCORE_PER_PLYR_FLAG_TOUCH, %plural); // z0dd - ZOD, 8/15/02. Message is pointless + } + else + { + if(isObject(%flag.grabber)) // is the grabber still here? + { + messageClient(%cl, 'msgCTFFriendCap', '\c0You receive %1 point%2 for capturing the enemy flag! %3 gets %4 point%5 for the steal assist.', %game.SCORE_PER_PLYR_FLAG_CAP, %plural, %flag.grabber.name, %game.SCORE_PER_PLYR_FLAG_TOUCH, %plural2); + messageClient(%flag.grabber, 'msgCTFFriendCap', '\c0You receive %1 point%2 for stealing a flag that was subsequently capped by %3.', %game.SCORE_PER_PLYR_FLAG_TOUCH, %plural2, %cl.name); + } + else + messageClient(%cl, 'msgCTFFriendCap', '\c0You receive %1 point%2 for capturing the enemy flag!', %game.SCORE_PER_PLYR_FLAG_CAP, %plural); + + //messageTeamExcept(%cl, 'msgCTFFriendCap', '\c0Teammate %1 receives %2 point%3 for capturing the enemy flag!', %cl.name, %game.SCORE_PER_PLYR_FLAG_CAP, %plural); // z0dd - ZOD, 8/15/02. Message is pointless + //messageTeam(%flag.team, 'msgCTFEnemyCap', '\c0Enemy %1 received %2 point%3 for capturing your flag!', %cl.name, %game.SCORE_PER_PLYR_FLAG_CAP, %plural); // z0dd - ZOD, 8/15/02. Message is pointless + } + // --------------------------------------------------- + } + + %game.recalcScore(%cl); + + if(isObject(%flag.grabber)) + %game.recalcScore(%flag.grabber); + + %game.checkScoreLimit(%cl.team); +} + + +function CTFGame::awardScoreFlagTouch(%game, %cl, %flag) +{ + %flag.grabber = %cl; + %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 %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, %perc) +{ + // --------------------------------------------------- + // z0dd - ZOD, 9/29/02. Removed T2 demo code from here + if (%game.SCORE_PER_FLAG_RETURN != 0) + { + %pts = mfloor( %game.SCORE_PER_FLAG_RETURN * (%perc/100) ); + if(%perc == 100) + messageClient(%cl, 'scoreFlaRetMsg', 'Flag return - exceeded capping distance - %1 point bonus.', %pts, %perc); + else if(%perc == 0) + messageClient(%cl, 'scoreFlaRetMsg', 'You gently place the flag back on the stand.', %pts, %perc); + else + messageClient(%cl, 'scoreFlaRetMsg', '\c0Flag return from %2%% of capping distance - %1 point bonus.', %pts, %perc); + %cl.returnPts += %pts; + } + %game.recalcScore(%cl); + return %game.SCORE_PER_FLAG_RETURN; + // --------------------------------------------------- +} + +function CTFGame::awardScoreStalemateReturn(%game, %cl) +{ + if (%game.SCORE_PER_STALEMATE_RETURN != 0) + { + messageClient(%cl, 'scoreStaleRetMsg', '\c0You received a %1 point bonus for a stalemate-breaking, flag return.', %game.SCORE_PER_STALEMATE_RETURN); + %cl.returnPts += %game.SCORE_PER_STALEMATE_RETURN; + } + %game.recalcScore(%cl); + return %game.SCORE_PER_STALEMATE_RETURN; +} + +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', '\c2%1 defended our generator from an attack.', %killerID.name); // z0dd - ZOD, 8/15/02. Tell team + //messageTeamExcept(%killerID, 'msgGenDef', '\c0Teammate %1 received a %2 point bonus for defending a generator.', %killerID.name, %game.SCORE_PER_GEN_DEFEND); + } + %game.recalcScore(%cl); + return %game.SCORE_PER_GEN_DEFEND; +} + +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', '\c2%1 stopped the enemy flag carrier.', %killerID.name); // z0dd - ZOD, 8/15/02. Tell team + //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); + return %game.SCORE_PER_CARRIER_KILL; +} + +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', '\c2%1 defended our flag.', %killerID.name); // z0dd - ZOD, 8/15/02. Tell team + //messageTeamExcept(%killerID, 'msgFlagDef', '\c0Teammate %1 received a %2 point bonus for defending your flag!', %killerID.name, %game.SCORE_PER_FLAG_DEFEND); + } + %game.recalcScore(%killerID); + return %game.SCORE_PER_FLAG_DEFEND; +} + +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', '\c2%1 protected our flag carrier.', %killerID.name); // z0dd - ZOD, 8/15/02. Tell team + //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); + return %game.SCORE_PER_ESCORT_ASSIST; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// Asset Destruction scoring //////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////// + +// z0dd - ZOD, 10/03/02. Penalty for TKing equiptment. +function CTFGame::awardScoreTkDestroy(%game, %cl, %obj) +{ + %cl.tkDestroys++; + messageTeamExcept(%cl, 'msgTkDes', '\c5Teammate %1 destroyed your team\'s %3 objective!', %cl.name, %game.cleanWord(%obj.getDataBlock().targetTypeTag)); + messageClient(%cl, 'msgTkDes', '\c0You have been penalized %1 points for destroying your teams equiptment.', %game.SCORE_PER_TK_DESTROY); + %game.recalcScore(%cl); + %game.shareScore(%cl, %game.SCORE_PER_TK_DESTROY); +} + +function CTFGame::awardScoreStaticShapeDestroy(%game, %cl, %obj) +{ + %dataName = %obj.getDataBlock().getName(); + switch$ ( %dataName ) + { + case "GeneratorLarge": + %cl.genDestroys++; + %value = %game.SCORE_PER_DESTROY_GEN; + %msgType = 'msgGenDes'; + %tMsg = '\c5%1 destroyed an enemy %2 Generator!'; + %clMsg = '\c0You received a %1 point bonus for destroying an enemy generator.'; + + case "SolarPanel": + %cl.solarDestroys++; + %value = %game.SCORE_PER_DESTROY_SOLAR; + %msgType = 'msgSolarDes'; + %tMsg = '\c5%1 destroyed an enemy %2 Solar Panel!'; + %clMsg = '\c0You received a %1 point bonus for destroying an enemy solar panel.'; + + case "SensorLargePulse" or "SensorMediumPulse": + %cl.sensorDestroys++; + %value = %game.SCORE_PER_DESTROY_SENSOR; + %msgType = 'msgSensorDes'; + %tMsg = '\c5%1 destroyed an enemy %2 Sensor!'; + %clMsg = '\c0You received a %1 point bonus for destroying an enemy pulse sensor.'; + + case "TurretBaseLarge": + %cl.turretDestroys++; + %value = %game.SCORE_PER_DESTROY_TURRET; + %msgType = 'msgTurretDes'; + %tMsg = '\c5%1 destroyed an enemy %2 Turret!'; + %clMsg = '\c0You received a %1 point bonus for destroying an enemy base turret.'; + + case "StationInventory": + %cl.IStationDestroys++; + %value = %game.SCORE_PER_DESTROY_ISTATION; + %msgType = 'msgInvDes'; + %tMsg = '\c5%1 destroyed an enemy %2 Inventory Station!'; + %clMsg = '\c0You received a %1 point bonus for destroying an enemy inventory station.'; + + case "StationAmmo": + %cl.aStationDestroys++; + %value = %game.SCORE_PER_DESTROY_ASTATION; + %msgType = 'msgAmmoDes'; + %tMsg = '\c5%1 destroyed an enemy %2 Ammo Station!'; + %clMsg = '\c0You received a %1 point bonus for destroying an enemy ammo station.'; + + case "StationVehicle": + %cl.VStationDestroys++; + %value = %game.SCORE_PER_DESTROY_VSTATION; + %msgType = 'msgVSDes'; + %tMsg = '\c5%1 destroyed an enemy Vehicle Station!'; + %clMsg = '\c0You received a %1 point bonus for destroying an enemy vehicle station.'; + + case "SentryTurret": + %cl.sentryDestroys++; + %value = %game.SCORE_PER_DESTROY_SENTRY; + %msgType = 'msgSentryDes'; + %tMsg = '\c5%1 destroyed an enemy %2 Sentry Turret!'; + %clMsg = '\c0You received a %1 point bonus for destroying an enemy sentry turret.'; + + case "DeployedMotionSensor" or "DeployedPulseSensor": + %cl.depSensorDestroys++; + %value = %game.SCORE_PER_DESTROY_DEP_SENSOR; + %msgType = 'msgDepSensorDes'; + %tMsg = '\c5%1 destroyed an enemy Deployed Sensor!'; + %clMsg = '\c0You received a %1 point bonus for destroying an enemy deployed sensor.'; + + case "TurretDeployedWallIndoor" or "TurretDeployedFloorIndoor" or "TurretDeployedCeilingIndoor": + %cl.depTurretDestroys++; + %value = %game.SCORE_PER_DESTROY_DEP_TUR; + %msgType = 'msgDepTurDes'; + %tMsg = '\c5%1 destroyed an enemy Deployed Spider Clamp Turret!'; + %clMsg = '\c0You received a %1 point bonus for destroying an enemy deployed spider clamp turret.'; + + case "TurretDeployedOutdoor": + %cl.depTurretDestroys++; + %value = %game.SCORE_PER_DESTROY_DEP_TUR; + %msgType = 'msgDepTurDes'; + %tMsg = '\c5%1 destroyed an enemy Deployed Landspike Turret!'; + %clMsg = '\c0You received a %1 point bonus for destroying an enemy deployed landspike turret.'; + + case "DeployedStationInventory": + %cl.depStationDestroys++; + %value = %game.SCORE_PER_DESTROY_DEP_INV; + %msgType = 'msgDepInvDes'; + %tMsg = '\c5%1 destroyed an enemy Deployed Station!'; + %clMsg = '\c0You received a %1 point bonus for destroying an enemy deployed station.'; + + case "MPBTeleporter": + %cl.mpbtstationDestroys++; + %value = %game.SCORE_PER_DESTROY_MPBTSTATION; + %msgType = 'msgMPBTeleDes'; + %tMsg = '\c5%1 destroyed an enemy MPB Teleport Station!'; + %clMsg = '\c0You received a %1 point bonus for destroying an enemy MPB teleport station.'; + + default: + return; + } + teamDestroyMessage(%cl, 'MsgDestroyed', %tMsg, %cl.name, %obj.nameTag); + messageClient(%cl, %msgType, %clMsg, %value, %dataName); + %game.recalcScore(%cl); + %game.shareScore(%scorer, %value); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// Repair Scoring Functions ///////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////// + +function CTFGame::testValidRepair(%game, %obj) +{ + if(!%obj.wasDisabled) + return false; + else if(%obj.lastDamagedByTeam == %obj.team) + return false; + else if(%obj.team != %obj.repairedBy.team) + return false; + else + { + if(%obj.soiledByEnemyRepair) + %obj.soiledByEnemyRepair = false; + + return true; + } +} + +function CTFGame::objectRepaired(%game, %obj, %objName) +{ + %game.staticShapeOnRepaired(%obj, %objName); + %obj.wasDisabled = false; +} + +function CTFGame::staticShapeOnRepaired(%game, %obj, %objName) +{ + if (%game.testValidRepair(%obj)) + { + %repairman = %obj.repairedBy; + %dataName = %obj.getDataBlock().getName(); + switch$ (%dataName) + { + case "GeneratorLarge": + %repairman.genRepairs++; + %score = %game.SCORE_PER_REPAIR_GEN; + %tMsgType = 'msgGenRepaired'; + %msgType = 'msgGenRep'; + %tMsg = '\c0%1 repaired the %2 Generator!'; + %clMsg = '\c0You received a %1 point bonus for repairing a generator.'; + + case "SolarPanel": + %repairman.solarRepairs++; + %score = %game.SCORE_PER_REPAIR_SOLAR; + %tMsgType = 'msgsolarRepaired'; + %msgType = 'msgsolarRep'; + %tMsg = '\c0%1 repaired the %2 Solar Panel!'; + %clMsg = '\c0You received a %1 point bonus for repairing a solar panel.'; + + case "SensorLargePulse" or "SensorMediumPulse": + %repairman.sensorRepairs++; + %score = %game.SCORE_PER_REPAIR_SENSOR; + %tMsgType = 'msgSensorRepaired'; + %msgType = 'msgSensorRep'; + %tMsg = '\c0%1 repaired the %2 Pulse Sensor!'; + %clMsg = '\c0You received a %1 point bonus for repairing a pulse sensor.'; + + case "StationInventory" or "StationAmmo": + %repairman.stationRepairs++; + %score = %game.SCORE_PER_REPAIR_ISTATION; + %tMsgType = 'msgStationRepaired'; + %msgType = 'msgIStationRep'; + %tMsg = '\c0%1 repaired the %2 Station!'; + %clMsg = '\c0You received a %1 point bonus for repairing a station.'; + + case "StationVehicle": + %repairman.VStationRepairs++; + %score = %game.SCORE_PER_REPAIR_VSTATION; + %tMsgType = 'msgvstationRepaired'; + %msgType = 'msgVStationRep'; + %tMsg = '\c0%1 repaired the Vehicle Station!'; + %clMsg = '\c0You received a %1 point bonus for repairing a vehicle station.'; + + case "TurretBaseLarge": + %repairman.TurretRepairs++; + %score = %game.SCORE_PER_REPAIR_TURRET; + %tMsgType = 'msgTurretRepaired'; + %msgType = 'msgTurretRep'; + %tMsg = '\c0%1 repaired the %2 Turret!'; + %clMsg = '\c0You received a %1 point bonus for repairing a base turret.'; + + case "SentryTurret": + %repairman.sentryRepairs++; + %score = %game.SCORE_PER_REPAIR_SENTRY; + %tMsgType = 'msgsentryTurretRepaired'; + %msgType = 'msgSentryRep'; + %tMsg = '\c0%1 repaired the %2 Sentry Turret!'; + %clMsg = '\c0You received a %1 point bonus for repairing a sentry turret.'; + + case "DeployedMotionSensor" or "DeployedPulseSensor": + %repairman.depSensorRepairs++; + %tMsgType = 'msgDepSensorRepaired'; + %msgType = 'msgDepSensorRep'; + %score = %game.SCORE_PER_REPAIR_DEP_SENSOR; + %tMsg = '\c0%1 repaired a Deployed Sensor!'; + %clMsg = '\c0You received a %1 point bonus for repairing a deployed sensor.'; + + case "TurretDeployedWallIndoor" or "TurretDeployedFloorIndoor" or "TurretDeployedCeilingIndoor": + %repairman.depTurretRepairs++; + %score = %game.SCORE_PER_REPAIR_DEP_TUR; + %tMsgType = 'msgDepTurretRepaired'; + %msgType = 'msgDepTurretRep'; + %tMsg = '\c0%1 repaired a Spider Clamp Turret!'; + %clMsg = '\c0You received a %1 point bonus for repairing a deployable spider clamp turret.'; + + case "TurretDeployedOutdoor": + %repairman.depTurretRepairs++; + %score = %game.SCORE_PER_REPAIR_DEP_TUR; + %tMsgType = 'msgDepTurretRepaired'; + %msgType = 'msgDepTurretRep'; + %tMsg = '\c0%1 repaired a Landspike Turret!'; + %clMsg = '\c0You received a %1 point bonus for repairing a deployable landspike turret.'; + + case "DeployedStationInventory": + %repairman.depInvRepairs++; + %score = %game.SCORE_PER_REPAIR_DEP_INV; + %tMsgType = 'msgDepInvRepaired'; + %msgType = 'msgDepInvRep'; + %tMsg = '\c0%1 repaired a Deployable Station!'; + %clMsg = '\c0You received a %1 point bonus for repairing a deployed station.'; + + case "MPBTeleporter": + %repairman.mpbtstationRepairs++; + %score = %game.SCORE_PER_REPAIR_MPBTSTATION; + %tMsgType = 'msgMPBTeleRepaired'; + %msgType = 'msgMPBTeleRep'; + %tMsg = '\c0%1 repaired the MPB Teleporter Station!'; + %clMsg = '\c0You received a %1 point bonus for repairing a mpb teleporter station.'; + + default: + return; + } + teamRepairMessage(%repairman, %tMsgType, %tMsg, %repairman.name, %obj.nameTag); + messageClient(%repairman, %msgType, %clMsg, %score, %dataName); + %game.recalcScore(%repairman); + } +} + +// --------------------------------------------------------- + +function CTFGame::resetScore(%game, %client) +{ + %client.offenseScore = 0; + %client.kills = 0; + %client.deaths = 0; + %client.suicides = 0; + %client.escortAssists = 0; + %client.teamKills = 0; + %client.tkDestroys = 0; // z0dd - ZOD, 10/03/02. Penalty for tking equiptment. + %client.flagCaps = 0; + %client.flagGrabs = 0; + %client.genDestroys = 0; + %client.sensorDestroys = 0; + %client.turretDestroys = 0; + %client.iStationDestroys = 0; + %client.vstationDestroys = 0; + %client.mpbtstationDestroys = 0; // z0dd - ZOD 3/30/02. MPB Teleporter + %client.solarDestroys = 0; + %client.sentryDestroys = 0; + %client.depSensorDestroys = 0; + %client.depTurretDestroys = 0; + %client.depStationDestroys = 0; + %client.vehicleScore = 0; + %client.vehicleBonus = 0; + + %client.flagDefends = 0; + %client.defenseScore = 0; + %client.genDefends = 0; + %client.carrierKills = 0; + %client.escortAssists = 0; + %client.turretKills = 0; + %client.mannedTurretKills = 0; + %client.flagReturns = 0; + %client.genRepairs = 0; + %client.SensorRepairs = 0; + %client.TurretRepairs = 0; + %client.StationRepairs = 0; + %client.VStationRepairs = 0; + %client.mpbtstationRepairs = 0; // z0dd - ZOD 3/30/02. MPB Teleporter + %client.solarRepairs = 0; + %client.sentryRepairs = 0; + %client.depSensorRepairs = 0; // z0dd - ZOD 5/27/03. Deployed sensors + %client.depInvRepairs = 0; + %client.depTurretRepairs = 0; + %client.returnPts = 0; + %client.score = 0; +} + +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; + + // ------------------------------------------------------------------------------ + // z0dd - ZOD - SquirrelOfDeath, 9/27/02. Delay on grabbing flag after tossing it + %player.flagTossWait = true; + %player.schedule(1000, resetFlagTossWait); + // ------------------------------------------------------------------------------ + + %client = %player.client; + %flag = %player.holdingFlag; + %flag.setVelocity("0 0 0"); + %flag.setTransform(%player.getWorldBoxCenter()); + %flag.setCollisionTimeout(%player); + + %held = %game.formatTime(getSimTime() - %game.flagHeldTime[%flag], false); // z0dd - ZOD, 8/15/02. How long did player hold flag? + + %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"); + + // z0dd - ZOD, 6/09/02. Remove anti-hover so flag can be thrown properly + %flag.static = false; + + // z0dd - ZOD, 10/02/02. Hack for flag collision bug. + %flag.searchSchedule = %game.schedule(10, "startFlagCollisionSearch", %flag); + + // 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); + + // z0dd - ZOD 3/30/02. Above message was sending the wrong varible to objective hud. + messageClient(%player.client, 'MsgCTFFlagDropped', '\c1You have left the mission area and lost the flag. (Held: %4)~wfx/misc/flag_drop.wav', %client.name, 0, %flag.team, %held); // Yogi, 8/18/02. 3rd param changed 0 -> %client.name + logEcho(%player.client.nameBase@" (pl "@%player@"/cl "@%player.client@") lost flag (out of bounds)"@" (Held: "@%held@")"); +} + +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); + + %enemyTeam = ( %destroyer.team == 1 ) ? 2 : 1; + + %scorer = 0; + %multiplier = 1; + + %passengers = 0; + for(%i = 0; %i < %data.numMountPoints; %i++) + if(%vehicle.getMountNodeObject(%i)) + %passengers++; + + //what destroyed this vehicle + if(%destroyer.client) + { + //it was a player, or his mine, satchel, whatever... + %destroyer = %destroyer.client; + %scorer = %destroyer; + + // determine if the object used was a mine + if(%vehicle.lastDamageType == $DamageType::Mine) + %multiplier = 2; + } + else if(%destroyer.getClassName() $= "Turret") + { + if(%destroyer.getControllingClient()) + { + //manned turret + %destroyer = %destroyer.getControllingClient(); + %scorer = %destroyer; + } + else + { + %destroyerName = "A turret"; + %multiplier = 0; + } + } + 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; + %scorer = %destroyer; + } + %multiplier = 3; + } + else // Is there anything else we care about? + return; + + + if(%destroyerName $= "") + %destroyerName = %destroyer.name; + + if(%vehicle.team == %destroyer.team) // team kill + { + %pref = (%vehicleType $= "Assault Tank") ? "an" : "a"; + messageAll( 'msgVehicleTeamDestroy', '\c0%1 TEAMKILLED %3 %2!', %destroyerName, %vehicleType, %pref); + } + + else // legit kill + { + //messageTeamExcept(%destroyer, 'msgVehicleDestroy', '\c0%1 destroyed an enemy %2.', %destroyerName, %vehicleType); // z0dd - ZOD, 8/20/02. not needed with new messenger on line below + teamDestroyMessage(%destroyer, 'msgVehDestroyed', '\c5%1 destroyed an enemy %2!', %destroyerName, %vehicleType); // z0dd - ZOD, 8/20/02. Send teammates a destroy message + messageTeam(%enemyTeam, 'msgVehicleDestroy', '\c0%1 destroyed your team\'s %2.', %destroyerName, %vehicleType); + //messageClient(%destroyer, 'msgVehicleDestroy', '\c0You destroyed an enemy %1.', %vehicleType); + + if(%scorer) + { + %value = %game.awardScoreVehicleDestroyed(%scorer, %vehicleType, %multiplier, %passengers); + %game.shareScore(%value); + } + } +} + +function CTFGame::awardScoreVehicleDestroyed(%game, %client, %vehicleType, %mult, %passengers) +{ + // z0dd - ZOD, 9/29/02. Removed T2 demo code from here + + if(%vehicleType $= "Grav Cycle") + %base = %game.SCORE_PER_DESTROY_WILDCAT; + else if(%vehicleType $= "Assault Tank") + %base = %game.SCORE_PER_DESTROY_TANK; + else if(%vehicleType $= "MPB") + %base = %game.SCORE_PER_DESTROY_MPB; + else if(%vehicleType $= "Turbograv") + %base = %game.SCORE_PER_DESTROY_SHRIKE; + else if(%vehicleType $= "Bomber") + %base = %game.SCORE_PER_DESTROY_BOMBER; + else if(%vehicleType $= "Heavy Transport") + %base = %game.SCORE_PER_DESTROY_TRANSPORT; + + %total = ( %base * %mult ) + ( %passengers * %game.SCORE_PER_PASSENGER ); + + %client.vehicleScore += %total; + + messageClient(%client, 'msgVehicleScore', '\c0You received a %1 point bonus for destroying an enemy %2.', %total, %vehicleType); + %game.recalcScore(%client); + return %total; +} + +function CTFGame::shareScore(%game, %client, %amount) +{ + // z0dd - ZOD, 9/29/02. Removed T2 demo code from here + + //error("share score of"SPC %amount SPC "from client:" SPC %client); + // all of the player in the bomber and tank share the points + // gained from any of the others + %vehicle = %client.vehicleMounted; + if(!%vehicle) + return 0; + %vehicleType = getTaggedString(%vehicle.getDataBlock().targetTypeTag); + if(%vehicleType $= "Bomber" || %vehicleType $= "Assault Tank") + { + for(%i = 0; %i < %vehicle.getDataBlock().numMountPoints; %i++) + { + %occupant = %vehicle.getMountNodeObject(%i); + if(%occupant) + { + %occCl = %occupant.client; + if(%occCl != %client && %occCl.team == %client.team) + { + // the vehicle has a valid teammate at this node + // share the score with them + %occCl.vehicleBonus += %amount; + %game.recalcScore(%occCl); + } + } + } + + } +} + +function CTFGame::awardScoreTurretKill(%game, %victimID, %implement) +{ + if ((%killer = %implement.getControllingClient()) != 0) //award whoever might be controlling the turret + { + if (%killer == %victimID) + %game.awardScoreSuicide(%victimID); + else if (%killer.team == %victimID.team) //player controlling a turret killed a teammate + { + %killer.teamKills++; + %game.awardScoreTurretTeamKill(%victimID, %killer); + %game.awardScoreDeath(%victimID); + } + else + { + %killer.mannedturretKills++; + %game.recalcScore(%killer); + %game.awardScoreDeath(%victimID); + } + } + else if ((%killer = %implement.owner) != 0) //if it isn't controlled, award score to whoever deployed it + { + if (%killer.team == %victimID.team) + { + %game.awardScoreDeath(%victimID); + } + else + { + %killer.turretKills++; + %game.recalcScore(%killer); + %game.awardScoreDeath(%victimID); + } + } + //default is, no one was controlling it, no one owned it. No score given. +} + +function CTFGame::testKill(%game, %victimID, %killerID) +{ + return ((%killerID !=0) && (%victimID.team != %killerID.team)); +} + +function CTFGame::awardScoreKill(%game, %killerID) +{ + %killerID.kills++; + %game.recalcScore(%killerID); + return %game.SCORE_PER_KILL; +} + + +function checkVehicleCamping( %team ) +{ + %position = $flagPos[%team]; + %radius = 5; + InitContainerRadiusSearch(%position, %radius, $TypeMasks::VehicleObjectType ); + + while ((%vehicle = containerSearchNext()) != 0) + { + %dist = containerSearchCurrRadDamageDist(); + + if (%dist > %radius) + continue; + else + { + //if( %vehicle.team == %team ) + applyVehicleCampDamage( %vehicle ); + } + } + + if( %team == 1 ) + Game.campThread_1 = schedule( 1000, 0, "checkVehicleCamping", 1 ); + else + Game.campThread_2 = schedule( 1000, 0, "checkVehicleCamping", 2 ); +} + +function applyVehicleCampDamage( %vehicle ) +{ + if( !isObject( %vehicle ) ) + return; + + if( %vehicle.getDamageState() $= "Destroyed" ) + return; + + %client = %vehicle.getMountNodeObject(0).client; // grab the pilot + + messageClient( %client, 'serverMessage', "Can't park vehicles in flag zones!" ); + %vehicle.getDataBlock().damageObject( %vehicle, 0, "0 0 0", 0.5, 0); +} + +// z0dd - ZOD, 10/02/02. Hack for flag collision bug. +function CTFGame::startFlagCollisionSearch(%game, %flag) +{ + %flag.searchSchedule = %game.schedule(10, "startFlagCollisionSearch", %flag); // SquirrelOfDeath, 10/02/02. Moved from after the while loop + %pos = %flag.getWorldBoxCenter(); + InitContainerRadiusSearch( %pos, 1.0, $TypeMasks::VehicleObjectType | $TypeMasks::CorpseObjectType | $TypeMasks::PlayerObjectType ); + while((%found = containerSearchNext()) != 0) + { + %flag.getDataBlock().onCollision(%flag, %found); + // SquirrelOfDeath, 10/02/02. Removed break to catch all players possibly intersecting with flag + } +} + +///////////////////////////////////////////////////////////////////////////////////////// +// VOTING /////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////// + +function CTFGame::sendGameVoteMenu(%game, %client, %key) +{ + DefaultGame::sendGameVoteMenu(%game, %client, %key); + if ( %game.scheduleVote $= "" ) + { + if(%client.isAdmin) + messageClient( %client, 'MsgVoteItem', "", %key, 'VoteAntiTurtleTime', 'change the anti turtle time to', 'Change Anti-Turtle time' ); + //else + // messageClient( %client, 'MsgVoteItem', "", %key, 'VoteAntiTurtleTime', 'change the anti turtle time to', 'Vote Anti-Turtle time' ); + } +} + +function CTFGame::evalVote(%game, %typeName, %admin, %arg1, %arg2, %arg3, %arg4) +{ + DefaultGame::evalVote(%game, %typeName, %admin, %arg1, %arg2, %arg3, %arg4); + switch$ (%typeName) + { + case "voteAntiTurtleTime": + %game.voteAntiTurtleTime(%admin, %arg1, %arg2, %arg3, %arg4); + } +} + +function CTFGame::sendAntiTurtleTimeList( %game, %client, %key ) +{ + messageClient( %client, 'MsgVoteItem', "", %key, 6, "", '6 minutes' ); + messageClient( %client, 'MsgVoteItem', "", %key, 8, "", '8 minutes' ); + messageClient( %client, 'MsgVoteItem', "", %key, 10, "", '10 minutes' ); + messageClient( %client, 'MsgVoteItem', "", %key, 12, "", '12 minutes' ); + messageClient( %client, 'MsgVoteItem', "", %key, 14, "", '14 minutes' ); + messageClient( %client, 'MsgVoteItem', "", %key, 16, "", '16 minutes' ); + messageClient( %client, 'MsgVoteItem', "", %key, 18, "", '18 minutes' ); + messageClient( %client, 'MsgVoteItem', "", %key, 200, "", 'Disable Anti Turtle' ); +} + +function CTFGame::voteAntiTurtleTime(%game, %admin, %newLimit) +{ + if( %newLimit == 200 ) + %display = "disabled"; + else + %display = %newLimit; + + %cause = ""; + if ( %admin ) + { + messageAll('MsgAdminForce', '\c3%1\c2 set the anti-turtle time to %2.~wfx/misc/diagnostic_on.wav', $AdminCl.name, %display); + $Host::ClassicAntiTurtleTime = %newLimit; + %cause = "(admin)"; + } + else + { + %totalVotes = %game.totalVotesFor + %game.totalVotesAgainst; + if(%totalVotes > 0 && (%game.totalVotesFor / (ClientGroup.getCount() - $HostGameBotCount)) > ($Host::VotePasspercent / 100)) + { + messageAll('MsgVotePassed', '\c2The anti-turtle time is set to %1.', %display); + $Host::ClassicAntiTurtleTime = %newLimit; + %cause = "(vote)"; + } + else + messageAll('MsgVoteFailed', '\c2The vote to change the anti-turtle time did not pass: %1 percent.', mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount) * 100)); + } + if(%cause !$= "") + logEcho($AdminCl.name @ ": anti-turtle time set to "@%display SPC %cause, 1); +} diff --git a/LakRabbitGame.cs b/LakRabbitGame.cs new file mode 100644 index 0000000..aaac4f8 --- /dev/null +++ b/LakRabbitGame.cs @@ -0,0 +1,2244 @@ +//------------------------------------------------------------------- +// Team Rabbit script +// ------------------------------------------------------------------- + +// DisplayName = LakRabbit + +//--- GAME RULES BEGIN --- +//Spawn with your favorites and double ammunition +//Rabbit is invincible for 2 seconds upon grab +//No laser, no chain. Eat it Altimor. +//Sounds for regular & special hits +//Points based on difficulty, speed, and distance +//Duel Mode forces rabbit to fight and rewards for killing +//--- GAME RULES END --- + +// Thanks for helping me test! +// maradona, pip, phantom jaguar, hilikus, the_ham, pip, wiggle, dragon, pancho villa, w/o, nectar and many others.. + +// v3.2 - Chocotaco April 2018 +// Made spawning closer to the Rabbit 200>150 +// Made Debrief font slightly larger +// Added flag updater code from CTF +// Took out Snipe and Chain Stats. Replaced them with more relevant stats. +// Added Unlimited DiscJump Variable. +// +// v3.1 9/11 Version +// Fixed Boundary walls +// Banned Certain types of weapons +// +// -2.17xxx points bug may be fixed. +// Fixed the Resetting... spam while toggling duel mode. +// Voting looks better (makes sense). +// No mysterious 'hoorah' when you get 100% mine on ground. +// Splash damage is based on how many are playing (more people less splash damage). Minimum 10% damage. No Splash is still votable. +// Heavies are easier to kill. +// Point adjustments. +// Flag wont just drop to the ground in duel mode. Basically, if you don't have enough upward momentum, it will be added to the flag so it can fly up. +// No 'duel kills' with missile special. +// Rabbit cannot disk jump in duel mode. +// DJ + Gren spam addressed again :) +// Throwing a hand nade removes invincibility. +// There is a minimum 'seconds per kill', to help with the 2 kills in 2 seconds scenario. +// Point maximum raised to 2000. +// Fixed No-Splash so you still take damage with direct hits on ground. +// Extra duel time for rabbit with No-Splash enabled and when it's 1on1 in duel mode. +// If you want to see the objective hud download LakRabbitObjHud.cs and put in /base/scripts/autoexec +// +// v3 +// Code optimization and bug fixes. +// Mortars are enabled, but only do half damage. Mortars can get long-distance or MA points. +// Long-distance shots are a little easier to get. +// You get one free disk-jump at spawn (DJ does no damage to you), but lose it if you get the flag. +// 'Specials'. You'll know it when you see it ;) +// No damage taken from mines if you aren't in the air, and mine damage in general is greatly reduced. +// [Fix] You only get points with sniper if you have 50% energy. +// Extra points for snipe headshot, and it is displayed with sound (regular snipe w/points has no sound). +// Points show up as soon as you get them (don't have to respawn) +// Distance and speed is now taken from where you shot, not where you are when the projectile hits. +// Duel mode -- rabbit has to fight or blow up, and is awarded points for killing people quickly (votable on/off). +// Grid is 'bouncy' like TR2 (code from TR2 as well -- thanks) for players and flags, and you can no longer spawn out of bounds. <-- may help with oob flag/ghost/sticky corpse bugs +// Ground damage cut in half in duel mode, except for rabbit. +// Messed with MA flag-grab points again, and it now shows your speed and height. +// Points for hand grenades. +// Hand nades don't detonate mines in duel mode (thx mista). +// You can vote off splash damage. Mines and grenades may still damage you with splash, however, and you can still get long distance shot points. +// You get points from chaingun every 5 hits (less spam, better looking points). Firing a different weapon resets your accuracy and hits same as before. +// Shocklance points are based mostly on speed and height now, and your height is displayed. +// Objective hud fixed. +// Spawn a little closer to rabbit, and the rabbit waypoint is only shown if someone grabs flag and is far away from you. +// Removed old AI Rabbit stuff (less console spam). +// DJ + Gren spam abuse addressed. Good luck getting points this way... +// New debriefing information at the end of a map: score, kills, MAs, avg speed and distance, overall chain accuracy, percent of snipe and shock hits. +// Getting an MA with shocklance and sniper is now based on if -you- are in the air, not the opponent. +// Point limit reduced to 1500. +// +// v2 +// On CTF maps (or maps with two or more flags) you can capture the flag for points when 6 people are playing. Only one flag can be in play at a time. Once capped, your flag gets returned and you immediately take the new flag. +// Mines are now available. +// Flag gets 'tossed' when carrier dies, no longer drops like a rock. +// Better messages, showing speed and distance. Distance bonuses are calculated into points; no multipliers. +// Spawn with full energy. +// MA flag-grab points are better calculated. +// Spawning has been redone so you spawn within a radius of 250m from either the rabbit or flag. +// Lots of new maps, all competition CTF maps, set up for LakRabbit (no generators, etc) +// Rabbit doesn't suffer the CG or Laser damage subtraction based on players. +// Point system revamped. +// 2500 point limit. +// Long-Distance disc and GL shots may give points (depending on difficulty of shot). +// Flag-skipping bug fixed. + +// v1 - See game rules. + +// Vars: +// $Host::ShowFlagIcon +// 0 - Don't show +// 1 - Scope when sensor visible +// 2 - Do not scope when sensor visible +// +// $Host::LakRabbitPubPro +// 0 - Disable LakPro features +// 1 - Enable LakPro features +// +// $Host::LakRabbitDuelMode +// 0 - Disable Duel Mode +// 1 - Enable Duel Mode +// +// $Host::LakRabbitNoSplashDamage +// 0 - Disable No Splash Dame +// 1 - Enable No Splash Damage +// +// $Host::ShowFlagTask +// 0 - Do not show flag task +// 1 - Show the flag when dropped as a task +// +// $Host::EnableLakUnlimitedDJ +// 0 - Players only get one DiscJump +// 1 - Show the flag when dropped as a task +// + + +package LakRabbitGame { + +function Flag::objectiveInit(%data, %flag) +{ + $flagStatus = ""; + %flag.carrier = ""; + %flag.originalPosition = %flag.getTransform(); + %flag.isHome = true; + %flag.rotate = true; + + // ilys -- add the icon to the flag + if($Host::ShowFlagIcon == 1) + { + %flag.scopeWhenSensorVisible(false); + setTargetSensorGroup(%flag.getTarget(), $NonRabbitTeam); + setTargetRenderMask(%flag.getTarget(), getTargetRenderMask(%flag.getTarget()) | 0x2); + setTargetAlwaysVisMask(%flag.getTarget(), 0x7); + } + + // create a waypoint to the flag's starting place + %flagWaypoint = new WayPoint() + { + position = %flag.position; + rotation = "1 0 0 0"; + name = "Flag Home"; + dataBlock = "WayPointMarker"; + team = $NonRabbitTeam; + }; + + $AIRabbitFlag = %flag; + + MissionCleanup.add(%flagWaypoint); + + // ------------------------------------------------------------------------------------------- + // z0dd - ZOD, 10/03/02. Use triggers for flags that are at home, hack for flag collision bug. + %flag.trigger = new Trigger() + { + dataBlock = flagTrigger; + polyhedron = "-0.6 0.6 0.1 1.2 0.0 0.0 0.0 -1.2 0.0 0.0 0.0 2.5"; + position = %flag.position; + rotation = %flag.rotation; + }; + MissionCleanup.add(%flag.trigger); + %flag.trigger.flag = %flag; + // ------------------------------------------------------------------------------------------- +} + +// AI consoel spam +function AIThrowObject(%object) +{ + return; +} +function AIGrenadeThrown(%object) +{ + return; +} +function AICorpseAdded(%corpse) +{ + return; +} + +// eat it ZP/Altimor +function ChaingunImage::onFire(%data,%obj,%slot){} +function SniperRifleImage::onFire(%data,%obj,%slot){} +function ShockLanceImage::onFire(%data, %obj, %slot) +{ + %p = parent::onFire(%data,%obj,%slot); + %obj.client.totalShocks++; + %obj.setInvincible(false); + return %p; +} +function ShapeBaseImageData::onFire(%data, %obj, %slot) +{ + if(%data.projectile $= ChaingunBullet) + $LakFired[%obj, ChaingunBullet, 0]++; + else + { + $LakFired[%obj, ChaingunBullet, 0] = 0; + $LakFired[%obj, ChaingunBullet, 1] = 0; + } + + %p = parent::onFire(%data, %obj, %slot); + %p.shotFrom = %obj.getWorldBoxCenter(); + %p.shotSpeed = getSpeed(%obj); + +// borlak -- remove height from grenades to get rid of DJ + gren spam abuse + switch$(%data.projectile) + { + case BasicGrenade: + %p.shotFrom = setWord(%p.shotFrom, 2, getWord(%p.shotFrom,2) - getHeight(%obj)); + } + return %p; +} + +// borlak -- this is going to be one ugly hack, but I can't find where a non radius damage +// object calls damageObject on a -hit-, therefore cannot pass on the projectiles data to +// damageObject and get it's true origin of fire +function ProjectileData::onExplode(%data, %proj, %pos, %mod) +{ + $lastObjExplode = %proj; + + parent::onExplode(%data, %proj, %pos, %mod); +} +// this too... +function detonateGrenade(%obj) +{ + %obj.shotSpeed = -1; + %obj.isHandNade = 1; + $lastObjExplode = %obj; + + parent::detonateGrenade(%obj); +} +function GrenadeThrown::onThrow(%this, %gren) +{ + %gren.sourceObject.setInvincible(false); + %gren.shotFrom = %gren.getWorldBoxCenter(); + parent::onThrow(%this, %gren); +} + +// borlak -- prevent getting hurt while knocked back (more fun) +function Armor::onImpact(%data, %playerObject, %collidedObject, %vec, %vecLen) +{ + if(%playerObject.knockback) + return; + parent::onImpact(%data, %playerObject, %collidedObject, %vec, %vecLen); +} +// borlak -- ma testing and points +function Armor::damageObject(%data, %targetObject, %sourceObject, %position, %amount, %damageType, %momVec, %mineSC) +{ + %distance = 0; + %energy = 0; + %velBonus = 0; + %sound = ""; + %defaultSound = '~wfx/misc/bounty_bonus.wav'; + %weapon = 0; + %points = 0; + %ma = 0; + %special = ""; + %long = 0; + %accuracy = ""; + %velstring = ""; + %maxDamage = 0; + %percentDam = 0; + + if(%targetObject.invincible || %targetObject.getState() $= "Dead") + return; + + // rabbit can't DJ in duel mode + if(Game.duelMode && %targetObject.holdingFlag && %targetObject == %sourceObject + && %damageType == $DamageType::Disc + && getWord(%targetObject.getMuzzleVector(0),2) < -0.7 + && !TestForMA(%sourceObject, 5)) + { + %sound = '~wfx/misc/missed.wav'; + %amount = 2.0; + %targetObject.blowup(); + } + + // Zeph - PubPro no DJ + + if(Game.PubPro && %targetObject == %sourceObject + && %damageType == $DamageType::Disc + && getWord(%targetObject.getMuzzleVector(0),2) < -0.7 + && !TestForMA(%sourceObject, 5)) + { + %sound = '~wfx/misc/missed.wav'; + %amount = 2.0; + %targetObject.blowup(); + %targetObject.scriptKill(); + } + + // hurting yourself, check for free DJ + if( isObject(%sourceObject) + && %sourceObject.getDataBlock().getClassName() $= "PlayerData" + && %targetObject.client == %sourceObject.client) + { + if(%damageType == $DamageType::Disc && %sourceObject.freeDJ) + { + %amount = 0; + %sourceObject.freeDJ--; + } + } + + %targetObject.lastDamageType = %damageType; + + // borlak -- MA, long distance, mine and grenade points + if( isObject(%sourceObject) + && %sourceObject.getDataBlock().getClassName() $= "PlayerData" && %targetObject.getDataBlock().getClassName() $= "PlayerData" + && %targetObject.client.team != %sourceObject.client.team + && %damageType) + { + $LakDamaged[%targetObject.client] = %sourceObject.client; + %targetObject.lastDamagedBy = %sourceObject; + %targetPosition = %targetObject.getWorldBoxCenter(); + + // borlak -- DJ + gren spam abuse, remove player height + if(%damageType == $DamageType::Grenade && !$lastObjExplode.isHandNade) + %targetPosition = setWord(%targetPosition, 2, getWord(%targetPosition,2) - getHeight(%targetObject)); + + if(%damageType == $DamageType::ShockLance || %damageType == $DamageType::Laser) + { + $lastObjExplode = 0; + %distance = VectorDist(%targetPosition, %sourceObject.getWorldBoxCenter()); + %vel = getSpeed(%sourceObject); + } + else + { + %maxDamage = $lastObjExplode.getDataBlock().directDamage ? $lastObjExplode.getDataBlock().directDamage : $lastObjExplode.getDataBlock().indirectDamage; + %distance = VectorDist(%targetPosition, $lastObjExplode.shotFrom); + if($lastObjExplode.shotSpeed < 0) + %vel = getSpeed(%sourceObject); + else + %vel = $lastObjExplode.shotSpeed; + + } + %percentDam = mFloor((%amount/%maxDamage)*100); + %energy = %sourceObject.getEnergyLevel()/%sourceObject.getDataBlock().maxEnergy; + %velTarget = getSpeed(%targetObject); + %distanceBonus = (mPow(%distance,1.35)/10)+1; + %longDistanceBonus = (mPow(%distance,1.10)/10)+1; + %velBonus = (mPow(%vel,1.125)/10)+1; + + // for shock and laser, you get MA points if -you- are in the air not the opponent + if(%damageType == $DamageType::ShockLance || %damageType == $DamageType::Laser) + %ma = TestForMA(%sourceObject, 6); + else + %ma = TestForMA(%targetObject, 6); + + // lower splash damage depending on how many people are playing.. duel mode only + if(Game.duelMode && %percentDam < 98 && $lastObjExplode && $lastObjExplode.getDataBlock().indirectDamage > 0) + { + %mult = 1-(ClientGroup.getCount()-1)/10; + if(%mult < 0.10) + %mult = 0.10; + %amount *= %sourceObject.holdingFlag ? 1 : %mult; + %maxDamage *= %sourceObject.holdingFlag ? 1 : %mult; + } + + // no splash damage vote + if(Game.noSplashDamage && %percentDam < 98 && $lastObjExplode && !$lastObjExplode.isHandNade && %damageType != $DamageType::Mine) + %amount = 0.0; + + if(%damageType == $DamageType::Laser && (%energy > 0.5 || %players > 7)) + { + %players = (ClientGroup.getCount()-1)/1.5; + %points = (%distance/75)+1; + + if(%ma) + %points *= 1.75; + + if(%targetObject.client.headshot) + { + %sound = %defaultSound; + %special = " Headshot"; + %points *= 1.75; + } + %sourceObject.client.totalSnipeHits++; + %weapon = "Snipe"; + } + else if(%damageType == $DamageType::Bullet) + { + // doesn't matter if it's MA + %ma = 0; + + %players = (ClientGroup.getCount()-1)/1.75; + + $LakFired[%sourceObject, ChaingunBullet, 1]++; + + if($LakFired[%sourceObject, ChaingunBullet, 1] % 5 == 0) + { + %accamount = mFloor(($LakFired[%sourceObject, ChaingunBullet, 1] / $LakFired[%sourceObject, ChaingunBullet, 0])*100); + %velBonus = 0; + %points = (%accamount/3)+1; + %accuracy = " [Accuracy:" @ %accamount @ "%]"; + + %sourceObject.client.totalChainAccuracy += %accamount; + %sourceObject.client.totalChainHits++; + } + %weapon = "Chaingun"; + } + else if(%damageType == $DamageType::Disc) + { + if(%ma && %percentDam >= 98) + { + %points = %distanceBonus; + %sound = %defaultSound; + } + if(%percentDam >= 25 && !%ma && %distance >= 80 && %velTarget > 20) + { + %points = %longDistanceBonus; + %velBonus /= 2; + %long = 1; + %sound = %defaultSound; + } + + // special knockback if you hit too close, max 15% chance (point blank).. 5% at 30meters, 1% chance for any MA + %chance = mFloor(15 - %distance/3); + if(%chance <= 0) %chance = 1; + if(%ma && getRandom(1,100) <= %chance) + { + if(%targetObject.holdingFlag) + Game.playerDroppedFlag(%targetObject); + if(%sourceObject.holdingFlag && Game.duelMode) + { + duelBonus(%sourceObject.client); + $LakDamaged[%targetObject.client] = 0; + } + + // lower damage and make invincible to ground damage to make it a little more fun + %amount = 0.01; + %targetObject.setKnockback(true); + %targetObject.schedule(15000, "setKnockback", false); + + %p = %targetObject.getWorldBoxCenter(); + %muzzleVec = %sourceObject.getMuzzleVector(0); + %impulseVec = VectorScale(%muzzleVec, 25000); + %targetObject.applyImpulse(%p, %impulseVec); + %sound = '~wfx/misc/slapshot.wav'; + } + %weapon = "Disc"; + } + else if(%damageType == $DamageType::Grenade && $lastObjExplode.isHandNade) + { + if(%percentDam > 20) + { + %accuracy = " [Accuracy:" @ %percentDam @ "%]"; + %points = (%percentDam/10)+1; + %velBonus = 0; + %sound = '~wfx/misc/coin.wav'; + + } + if(%percentDam >= 99) + { + %sound = '~wfx/misc/Cheer.wav'; + %points *= 2.0; + } + if(%ma) + %points *= 1.5; + + %weapon = "Hand-Nade"; + } + else if(%damageType == $DamageType::Grenade) + { + if(Game.PubPro) + { + if(%ma && %percentDam >= 98) + { + %points = %distanceBonus; + %sound = %defaultSound; + } + if(%percentDam >= 25 && !%ma && %distance >= 80 && %velTarget > 20) + { + %points = %longDistanceBonus; + %velBonus /= 2; + %long = 1; + %sound = %defaultSound; + } + } + else + { + if(%ma && %percentDam >= 98) + { + %points = %distanceBonus/1.85; + %sound = %defaultSound; + } + if(%percentDam >= 25 && !%ma && %distance >= 100 && %velTarget > 30) + { + %points = %longDistanceBonus/1.85; + %velBonus /= 4; + %long = 1; + %sound = %defaultSound; + } + } + %weapon = "Grenade-Launcher"; + } + else if(%damageType == $DamageType::Mortar) + { + if(%ma && %percentDam >= 98) + { + %points = %distanceBonus*2.66; + %sound = %defaultSound; + } + if(%percentDam >= 25 && !%ma && %distance >= 100 && %velTarget > 30) + { + %points = %longDistanceBonus; + %velBonus /= 2; + %long = 1; + %sound = %defaultSound; + } + if(!Game.duelMode) + %amount /= 2; + %weapon = "Mortar"; + } + else if(%damageType == $DamageType::Mine) + { + %amount /= %amount > 0 ? 3 : 1; + + if(%ma) + { + %accuracy = " [Accuracy:" @ %percentDam @ "%]"; + %points = (%percentDam/7)+2; + %velBonus = 0; + %sound = '~wfx/misc/coin.wav'; + + if(%percentDam >= 99) + { + %sound = '~wfx/misc/Cheer.wav'; + %points *= 2.0; + } + } + if(!%ma) + %amount = 0; + %weapon = "MINE"; + } + else if(%damageType == $DamageType::ShockLance) + { + %height = getHeight(%sourceObject); + %heightBonus = (mPow(%height,1.20)/10)+1; + %velBonus /= 2; + %points = mFloor(%distance/2) + (%heightBonus); + + %accuracy = " [Height:" @ %height @"m]"; + // borlak -- check rear shocklance hit + %muzzlePos = %sourceObject.getMuzzlePoint(0); + %forwardVec = %targetObject.getForwardVector(); + %objDir2D = getWord(%forwardVec, 0) @ " " @ getWord(%forwardVec,1) @ " " @ "0.0"; + %objPos = %targetObject.getPosition(); + %dif = VectorSub(%objPos, %muzzlePos); + %dif = getWord(%dif, 0) @ " " @ getWord(%dif, 1) @ " 0"; + %dif = VectorNormalize(%dif); + %dot = VectorDot(%dif, %objDir2D); + + if(%dot >= mCos(1.05)) + { + if(%sourceObject.holdingFlag && TestForMA(%targetObject, 6)) + { + %sourceObject.setCloaked(true); + if($host::dontcloakflag) + %sourceObject.holdingFlag.setCloaked(false); + } + + %points *= 2; + %special = "-in-the-back"; + } + if(%ma) + %points += 3; + %sound = %defaultSound; + %sourceObject.client.totalShockHits++; + %weapon = "ShockLance"; + } + else if(%damageType == $DamageType::Blaster) + { + if(%ma) + { + %points = %distanceBonus/2; + %velBonus /= 2; + %sound = %defaultSound; + } + %weapon = "Blaster"; + } + else if(%damageType == $DamageType::Plasma) + { + if(%ma && %percentDam >= 98) + { + %points = %distanceBonus/1.3+2; + %sound = %defaultSound; + } + if(%percentDam >= 25 && !%ma && %distance >= 40 && %velTarget > 30) + { + %points = %longDistanceBonus*1.5; + %velBonus /= 1.75; + %long = 1; + %sound = %defaultSound; + } + %weapon = "Plasma"; + } + } + + // borlak -- recalc score for hits + if( isObject(%sourceObject) + && %sourceObject.getDataBlock().getClassName() $= "PlayerData" && %targetObject.getDataBlock().getClassName() $= "PlayerData" + && %points) + { + %distance = mFloor(%distance); + %points += %velBonus; + %points = mFloor(%points); + %sourceObject.client.morepoints += %points; + + if(%ma) + { + %sourceObject.client.mas++; + %sourceObject.client.totalSpeed += %vel; + %sourceObject.client.totalDistance += %distance; + %hitType = "Mid-Air "; + } + else if(%long) + %hitType = "Long-Distance "; + else + %hitType = ""; + + messageClient(%sourceObject.client,'msgPlrPointBonus', '\c4You receive %1 point%2! [%3%4%5] [Distance:%6m] [Speed:%7kph] %8', + %points, %points == 1 ? "" : "s", + %hitType, + %weapon, + %special !$= "" ? %special : "", + %distance, + %vel, + %accuracy); + messageAllExcept(%sourceObject.client, -1, 'msgRabbitPointBonus', '\c4%1 receives %2 point%3! [%4%5%6] [Distance:%7m] [Speed:%8kph] %9', + %sourceObject.client.name, + %points, %points == 1 ? "" : "s", + %hitType, + %weapon, + %special !$= "" ? %special : "", + %distance, + %vel, + %accuracy ); + + if(%sourceObject.holdingFlag && %points >= 75) + { + missileEveryone(%sourceObject); + %sound = '~wfx/Bonuses/horz_straipass2_heist.wav'; + } + + Game.recalcScore(%sourceObject.client); + } + +// borlak -- make a sound when you hit someone + if(%sound $= "" && %sourceObject.client.team != %targetObject.client.team) + messageClient(%sourceObject.client,'MsgHitSound','~wfx/misc/diagnostic_beep.wav'); + else if(%sound !$= "") + messageAll('msgSpecialHitSound', %sound); + +// borlak -- rabbit should be able to kill heavies/mediums fast(er) in duel mode + if(%targetObject.client.armor $= "Heavy" || %targetObject.client.armor $= "Medium") + { + if(Game.duelMode && %targetObject != %sourceObject && %sourceObject.holdingFlag) + %amount *= 2; + else + %amount *= 1.5; + } + parent::damageObject(%data, %targetObject, %sourceObject, %position, %amount, %damageType, %momVec, %mineSC); +} + +function deployMineCheck(%mineObj, %player) +{ + // explode it vgc + schedule(2000, %mineObj, "explodeMine", %mineObj, true); +} +// thanks mista +function MineDeployed:: damageObject(%data, %targetObject, %sourceObject, %position, %amount, %damageType) +{ +// NO NIFTY NADE/MINE SCRIPTS + if (Game.duelMode && %damageType == $DamageType::Grenade && $lastObjExplode.isHandNade) + return; + + parent:: damageObject(%data, %targetObject, %sourceObject, %position, %amount, %damageType); +} +function MineDeployed::onThrow(%this, %mine, %thrower) +{ + %mine.shotFrom = %mine.getWorldBoxCenter(); + parent::onThrow(%this, %mine, %thrower); +} +function MineDeployed::onDestroyed(%data, %obj, %lastState) +{ + %obj.shotFrom = %obj.getWorldBoxCenter(); + %obj.shotSpeed = -1; + $lastObjExplode = %obj; + + parent::onDestroyed(%data, %obj, %lastState); +} + +function Player::maxInventory(%this, %data) +{ + %max = ShapeBase::maxInventory(%this,%data) * 2; + if (%this.getInventory(AmmoPack)) + %max += AmmoPack.max[%data.getName()]; + return %max; +} + +function Player::setKnockback(%this, %val) +{ + %this.knockback = %val; +} + +}; + +//exec the AI scripts +//exec("scripts/aiRabbit.cs"); + +$InvBanList[LakRabbit, "TurretOutdoorDeployable"] = 1; +$InvBanList[LakRabbit, "TurretIndoorDeployable"] = 1; +$InvBanList[LakRabbit, "ElfBarrelPack"] = 1; +$InvBanList[LakRabbit, "MortarBarrelPack"] = 1; +$InvBanList[LakRabbit, "PlasmaBarrelPack"] = 1; +$InvBanList[LakRabbit, "AABarrelPack"] = 1; +$InvBanList[LakRabbit, "MissileBarrelPack"] = 1; +$InvBanList[LakRabbit, "MissileLauncher"] = 1; +$InvBanList[LakRabbit, "MotionSensorDeployable"] = 1; +$InvBanList[LakRabbit, "PulseSensorDeployable"] = 1; +$InvBanList[LakRabbit, "ELFGun"] = 1; +$InvBanList[LakRabbit, "CameraGrenade"] = 1; +$InvBanList[LakRabbit, "FlareGrenade"] = 1; +$InvBanList[LakRabbit, "FlashGrenade"] = 1; +$InvBanList[LakRabbit, "ConcussionGrenade"] = 1; + +// PubPro +$InvBanList[LakRabbit, "AmmoPack"] = 1; +$InvBanList[LakRabbit, "CloakingPack"] = 1; +$InvBanList[LakRabbit, "RepairPack"] = 1; +$InvBanList[LakRabbit, "SatchelCharge"] = 1; +$InvBanList[LakRabbit, "SensorJammerPack"] = 1; +$InvBanList[LakRabbit, "ShieldPack"] = 1; + +$InvBanList[LakRabbit, "SniperRifle"] = 1; +$InvBanList[LakRabbit, "Chaingun"] = 1; + +$InvBanList[LakRabbit, "GrenadeLauncher"] = $Host::LakRabbitPubPro; +$InvBanList[LakRabbit, "ShockLance"] = $Host::LakRabbitPubPro; +$InvBanList[LakRabbit, "Mortar"] = $Host::LakRabbitPubPro; +$InvBanList[LakRabbit, "Grenade"] = $Host::LakRabbitPubPro; + +$InvBanList[LakRabbit, "Disc"] = 0; +$InvBanList[LakRabbit, "Plasma"] = 0; +$InvBanList[LakRabbit, "Blaster"] = 0; +$InvBanList[LakRabbit, "EnergyPack"] = 0; +$InvBanList[LakRabbit, "Mine"] = 0; +$InvBanList[LakRabbit, "TargetingLaser"] = 0; + + +// borlak functions +function TestForMA(%player, %distance) +{ + %mask = $TypeMasks::StaticShapeObjectType | $TypeMasks::InteriorObjectType | $TypeMasks::TerrainObjectType; + %rayStart = %player.getWorldBoxCenter(); + %rayEnd = getWord(%rayStart, 0) SPC getWord(%rayStart, 1) SPC getWord(%rayStart, 2) - %distance; + %ground = ContainerRayCast(%rayStart, %rayEnd, %mask, 0); + + return !%ground; +} + +function getSpeed(%obj) +{ + return mFloor(VectorLen(%obj.getVelocity())*3.6); +} + +function PlayingPlayers() +{ + %players = 0; + for (%i = 0; %i < ClientGroup.getCount(); %i++) + { + %cl = ClientGroup.getObject(%i); + if(isObject(%cl.player) && %cl.player.team > 0) + %players++; + } + return %players; +} + +function duelBonus(%client) +{ + %client.duelSeconds += 12; + if(Game.noSplashDamage) + %client.duelSeconds += 5; + %client.duelKills++; + messageClient(%client, 'MsgDuelBonus', '\c4You now have %1 seconds to live.', %client.duelSeconds); +} + +//for(%i = 0; %i < ClientGroup.getCount(); %i++){%target = ClientGroup.getObject(%i).player; if(%target.holdingFlag) missileEveryone(%target);} + +// awards/tricks/fun things +function missileEveryone(%attacker) +{ + for(%i = 0; %i < ClientGroup.getCount(); %i++) + { + %target = ClientGroup.getObject(%i); + + if(!%target.player || %target.player == %attacker) + continue; + + %p = ShapeBaseImageData::onFire(MissileLauncherImage,%attacker,0); + MissileSet.add(%p); + %p.setObjectTarget(%target.player); + %attacker.cantFire = ""; + } + + // make him invincible so he doesn't get killed and ruin the effect! + %attacker.setInvincible(true); + %attacker.schedule(5000, "setInvincible", false); + + // give the rabbit some more duel seconds if it's duel mode.. + if(Game.duelMode && %attacker.holdingFlag) + %attacker.client.duelSeconds += 15; +} +function killEveryone(%ignore, %message) +{ + if(!%message) + messageAll('msgKillEveryone', 'Resetting...'); + else + messageAll('msgKillEveryone', %message); + + for(%i = 0; %i < ClientGroup.getCount(); %i++) + { + %target = ClientGroup.getObject(%i); + + if(!%target.player || %target.player == %ignore) + continue; + + %target.player.blowup(); + %target.player.scriptKill(); + } +} + +function checkDuelTimer(%client) +{ + %client.duelTimer = schedule(1000, 0, checkDuelTimer, %client); + + if(PlayingPlayers() < 2) + return; + + %client.duelSeconds--; + %client.duelSecondsCounted++; + + if(%client.duelSeconds <= 0) + { + cancel(%client.duelTimer); + + messageAll('MsgDuelTimeout', '\c2%1 failed to kill within the alloted time!~wfx/misc/whistle.wav', %client.name); + %client.player.blowup(); + %client.player.scriptKill(); + } + else + { + if(%client.duelSeconds == 10 || %client.duelSeconds == 5 || %client.duelSeconds <= 3) + { + %plural = (%client.duelSeconds != 1 ? 's' : ""); + messageClient(%client, 'MsgDuelTimer', '\c4[Duel Mode] You have %1 second%2 to kill someone, or die!~wfx/misc/red_alert_short.wav', %client.duelSeconds, %plural); + } + } +} + +function setFlagDeny(%client, %value) +{ + %client.flagDeny = %value; +} + +// new "modes" and voting functions +$VoteMessage["VoteDuelMode"] = "turn"; +$VoteMessage["VoteSplashDamage"] = "turn"; +$VoteMessage["VotePro"] = "turn"; + +function LakRabbitGame::sendGameVoteMenu( %game, %client, %key ) +{ + parent::sendGameVoteMenu( %game, %client, %key ); + + %isAdmin = ( %client.isAdmin || %client.isSuperAdmin ); + + if( %game.scheduleVote $= "" ) + { + if(%isAdmin) + { + if(!Game.duelMode) + messageClient( %client, 'MsgVoteItem', "", %key, 'VoteDuelMode', 'enable duel mode', 'Duel Mode On' ); + else + messageClient( %client, 'MsgVoteItem', "", %key, 'VoteDuelMode', 'disable duel mode', 'Duel Mode Off' ); + + if(!Game.noSplashDamage) + messageClient( %client, 'MsgVoteItem', "", %key, 'VoteSplashDamage', 'disable splash damage', 'Splash Damage Off' ); + else + messageClient( %client, 'MsgVoteItem', "", %key, 'VoteSplashDamage', 'enable splash damage', 'Splash Damage On' ); + // DeVast - PubPro votes + if(!Game.PubPro) + messageClient( %client, 'MsgVoteItem', "", %key, 'VotePro', 'enable Pro', 'Turn Pro On' ); + else + messageClient( %client, 'MsgVoteItem', "", %key, 'VotePro', 'disable Pro', 'Turn Pro Off' ); + } + else + { + if(!Game.duelMode) + messageClient( %client, 'MsgVoteItem', "", %key, 'VoteDuelMode', 'Duel Mode on', 'Vote to turn on Duel Mode' ); + else + messageClient( %client, 'MsgVoteItem', "", %key, 'VoteDuelMode', 'Duel Mode off', 'Vote to turn off Duel Mode' ); + + if(!Game.noSplashDamage) + messageClient( %client, 'MsgVoteItem', "", %key, 'VoteSplashDamage', 'Splash Damage Off', 'Vote to turn Splash Damage Off' ); + else + messageClient( %client, 'MsgVoteItem', "", %key, 'VoteSplashDamage', 'Splash Damage On', 'Vote to turn Splash Damage On' ); + // DeVast - PubPro votes + if(!Game.PubPro) + messageClient( %client, 'MsgVoteItem', "", %key, 'VotePro', 'enable Pro', 'Turn Pro On' ); + else + messageClient( %client, 'MsgVoteItem', "", %key, 'VotePro', 'disable Pro', 'Turn Pro Off' ); + } + } +} + +function LakRabbitGame::evalVote(%game, %typeName, %admin, %arg1, %arg2, %arg3, %arg4) +{ + switch$ (%typeName) + { + case "voteDuelMode": + %game.voteDuelMode(%admin, %arg1, %arg2, %arg3, %arg4); + case "voteSplashDamage": + %game.voteSplashDamage(%admin, %arg1, %arg2, %arg3, %arg4); + case "VotePro": + %game.VotePro(%admin, %arg1, %arg2, %arg3, %arg4); + } + + parent::evalVote(%game, %typeName, %admin, %arg1, %arg2, %arg3, %arg4); +} + +// Zeph - PubPro voting + +function LakRabbitGame::VotePro(%game, %admin, %arg1, %arg2, %arg3, %arg4) +{ + if(%admin) + { + killeveryone(); + + if(%game.PubPro) + { + messageAll('MsgAdminForce', '\c2The Admin has disabled Pro.'); + + $InvBanList[LakRabbit, "GrenadeLauncher"] = 0; + $InvBanList[LakRabbit, "ShockLance"] = 0; + $InvBanList[LakRabbit, "Mortar"] = 0; + $InvBanList[LakRabbit, "Grenade"] = 0; + + %game.PubPro = false; + } + else + { + messageAll('MsgAdminForce', '\c2The Admin has enabled Pro.'); + + $InvBanList[LakRabbit, "GrenadeLauncher"] = 1; + $InvBanList[LakRabbit, "ShockLance"] = 1; + $InvBanList[LakRabbit, "Mortar"] = 1; + $InvBanList[LakRabbit, "Grenade"] = 1; + + %game.PubPro = true; + } + } + else + { + %totalVotes = %game.totalVotesFor + %game.totalVotesAgainst; + if(%totalVotes > 0 && (%game.totalVotesFor / ClientGroup.getCount()) > ($Host::VotePasspercent / 100)) + { + killeveryone(); + + if(%game.PubPro) + { + messageAll('MsgVotePassed', '\c2PubPro Disabled.'); + + $InvBanList[LakRabbit, "GrenadeLauncher"] = 0; + $InvBanList[LakRabbit, "ShockLance"] = 0; + $InvBanList[LakRabbit, "Mortar"] = 0; + $InvBanList[LakRabbit, "Grenade"] = 0; + + %game.PubPro = false; + } + else + { + messageAll('MsgVotePassed', '\c2PubPro Enabled.'); + + $InvBanList[LakRabbit, "GrenadeLauncher"] = 1; + $InvBanList[LakRabbit, "ShockLance"] = 1; + $InvBanList[LakRabbit, "Mortar"] = 1; + $InvBanList[LakRabbit, "Grenade"] = 1; + + %game.PubPro = true; + } + } + else + messageAll('MsgVoteFailed', '\c2Mode change did not pass: %1 percent.', mFloor(%game.totalVotesFor/ClientGroup.getCount() * 100)); + } + + $Host::LakRabbitPubPro = %game.PubPro; +} + +function LakRabbitGame::voteDuelMode(%game, %admin, %arg1, %arg2, %arg3, %arg4) +{ + if(%admin) + { + killEveryone(); + if(%game.duelMode) + { + messageAll('MsgAdminForce', '\c2The Admin has disabled Duel Mode.'); + %game.duelMode = false; + } + else + { + messageAll('MsgAdminForce', '\c2The Admin has enabled Duel Mode.'); + %game.duelMode = true; + } + } + else + { + %totalVotes = %game.totalVotesFor + %game.totalVotesAgainst; + if(%totalVotes > 0 && (%game.totalVotesFor / ClientGroup.getCount()) > ($Host::VotePasspercent / 100)) + { + killEveryone(); + if(%game.duelMode) + { + messageAll('MsgVotePassed', '\c2Duel Mode Disabled.'); + %game.duelMode = false; + } + else + { + messageAll('MsgVotePassed', '\c2Duel Mode Enabled.'); + %game.duelMode = true; + } + } + else + messageAll('MsgVoteFailed', '\c2Mode change did not pass: %1 percent.', mFloor(%game.totalVotesFor/ClientGroup.getCount() * 100)); + } + + // save + $Host::LakRabbitDuelMode = %game.duelMode; +} + +function LakRabbitGame::voteSplashDamage(%game, %admin, %arg1, %arg2, %arg3, %arg4) +{ + if(%admin) + { + if(%game.noSplashDamage) + { + messageAll('MsgAdminForce', '\c2The Admin has turned on Splash Damage.'); + %game.noSplashDamage = false; + } + else + { + messageAll('MsgAdminForce', '\c2The Admin has turned off Splash Damage.'); + %game.noSplashDamage = true; + } + } + else + { + %totalVotes = %game.totalVotesFor + %game.totalVotesAgainst; + if(%totalVotes > 0 && (%game.totalVotesFor / ClientGroup.getCount()) > ($Host::VotePasspercent / 100)) + { + if(%game.noSplashDamage) + { + messageAll('MsgVotePassed', '\c2Splash Damage Turned On.'); + %game.noSplashDamage = false; + } + else + { + messageAll('MsgVotePassed', '\c2Splash Damage Turned Off.'); + %game.noSplashDamage = true; + } + } + else + messageAll('MsgVoteFailed', '\c2Mode change did not pass: %1 percent.', mFloor(%game.totalVotesFor/ClientGroup.getCount() * 100)); + } + + // save + $Host::LakRabbitNoSplashDamage = %game.noSplashDamage; +} + +// new debriefing stuff +function LakRabbitGame::sendDebriefing( %game, %client ) +{ + messageClient( %client, 'MsgDebriefAddLine', "", 'PLAYERSCOREKILLSMASSPEEDDISTTOT DISTSHOCKSL HITS' ); + // %cl.name, %score, %kills, %mas, %avgSpeed, %avgDistance, %alltotdistance, %shockPercent, %totshockhits + // Scores: + %totscore = 0; + %totkills = 0; + %totmas = 0; + %totspeed = 0; + %totdistance = 0; + //%totchainacc = 0; + //%totsnipepercent= 0; + %totshockpercent= 0; + %speeds = 0; + %dists = 0; + //%chains = 0; + //%snipes = 0; + %shocks = 0; + %alltotdistance = 0; + %totshockhits = 0; + + %count = $TeamRank[0, count]; + for(%i = 0; %i < %count; %i++) + { + // Send the debrief line: + %cl = $TeamRank[0, %i]; + + if(%cl.score == 0) %score = 0; + else %score = %cl.score; + if(%cl.kills == 0) %kills = 0; + else %kills = %cl.kills; + if(%cl.mas == 0) %mas = 0; + else %mas = %cl.mas; + + //if(%cl.totalSnipes == 0) %cl.totalSnipes = 1; + if(%cl.totalShocks == 0) %cl.totalShocks = 1; + + if(%cl.totalSpeed == 0) %avgSpeed = 0; + else %avgSpeed = mFloor(%cl.totalSpeed/%cl.mas); + if(%cl.totalDistance == 0) %avgDistance = 0; + else %avgDistance = mFloor(%cl.totalDistance/%cl.mas); + //if(%cl.totalChainAccuracy == 0) %avgChainAcc = 0; + //else %avgChainAcc = mFloor(%cl.totalChainAccuracy/%cl.totalChainHits); + //if(%cl.totalSnipeHits == 0) %snipePercent = 0; + //else %snipePercent = mFloor(%cl.totalSnipeHits/%cl.totalSnipes*100); + if(%cl.totalShockHits == 0) %shockPercent = 0; + else %shockPercent = mFloor(%cl.totalShockHits/%cl.totalShocks*100); + if(%cl.totalDistance == 0) %othertotdistance = 0; + else %othertotdistance = mFloor(%cl.totalDistance); + if(%cl.totalShockHits == 0) %shockhits = 0; + else %shockhits = mFloor(%cl.totalShockHits); + messageClient( %client, 'MsgDebriefAddLine', "", ' %1%2%3%4%5%6%7%8%%%9', + %cl.name, %score, %kills, %mas, %avgSpeed, %avgDistance, %othertotdistance, %shockPercent, %shockhits); + + if(%score) %totscore += %score; + if(%kills) %totkills += %kills; + if(%mas) %totmas += %mas; + if(%avgSpeed){ %totspeed += %avgSpeed; %speeds++; } + if(%avgDistance){ %totdistance += %avgDistance; %dists++; } + //if(%avgChainAcc){ %totchainacc += %avgChainAcc; %chains++; } + //if(%snipePercent){ %totsnipepercent += %snipePercent; %snipes++; } + if(%shockPercent){ %totshockpercent += %shockPercent; %shocks++; } + if(%othertotdistance){ %alltotdistance += %othertotdistance; } + if(%shockhits){ %totshockhits += %shockhits; } + + } + + messageClient( %client, 'MsgDebriefAddLine', "", '%1%2%3%4%5%6%7%8%%%9\n', + " Totals:", %totscore, %totkills, %totmas, mFloor(%totspeed/%speeds), mFloor(%totdistance/%dists), %alltotdistance, mFloor(%totshockpercent/%shocks), %totshockhits); +} + +// regular game functions +function LakRabbitGame::setUpTeams(%game) +{ + // Force the numTeams variable to one: + DefaultGame::setUpTeams(%game); + %game.numTeams = 1; + setSensorGroupCount(3); + + //team damage should always be off for Rabbit + $teamDamage = 0; + + //make all the sensor groups visible at all times + if (!Game.teamMode) + { + setSensorGroupAlwaysVisMask($NonRabbitTeam, 0xffffffff); + setSensorGroupAlwaysVisMask($RabbitTeam, 0xffffffff); + + // non-rabbits can listen to the rabbit: all others can only listen to self + setSensorGroupListenMask($NonRabbitTeam, (1 << $RabbitTeam) | (1 << $NonRabbitTeam)); + } +} + +function LakRabbitGame::initGameVars(%game) +{ + %game.playerBonusValue = 1; + %game.playerBonusTime = 3 * 1000; + + %game.teamBonusValue = 3; + %game.teamBonusTime = 5 * 1000; + %game.flagReturnTime = 25 * 1000; + + %game.waypointFrequency = 24000; + %game.waypointDuration = 6000; + + %game.duelMode = $Host::LakRabbitDuelMode; + %game.PubPro = $Host::LakRabbitPubPro; + %game.noSplashDamage = $Host::LakRabbitNoSplashDamage; +} + +$RabbitTeam = 2; +$NonRabbitTeam = 1; + +// ----- These functions supercede those in DefaultGame.cs + +function LakRabbitGame::allowsProtectedStatics(%game) +{ + return true; +} + +function LakRabbitGame::clientMissionDropReady(%game, %client) +{ + messageClient(%client, 'MsgClientReady', "", %game.class); + messageClient(%client, 'MsgYourScoreIs', "", 0); + //messageClient(%client, 'MsgYourRankIs', "", -1); + messageClient(%client, 'MsgRabbitFlagStatus', "", $flagStatus); + + messageClient(%client, 'MsgMissionDropInfo', '\c0You are in mission %1 (%2).', $MissionDisplayName, $MissionTypeDisplayName, $ServerName ); + + DefaultGame::clientMissionDropReady(%game,%client); +} + +function LakRabbitGame::AIHasJoined(%game, %client) +{ + //let everyone know the player has joined the game + //messageAllExcept(%client, -1, 'MsgClientJoinTeam', '%1 has joined the hunt.', %client.name, "", %client, $NonRabbitTeam); +} + +function LakRabbitGame::clientJoinTeam( %game, %client, %team, %respawn ) +{ + %game.assignClientTeam( %client ); + + // Spawn the player: + %game.spawnPlayer( %client, %respawn ); + %game.recalcScore( %client ); +} + + +function LakRabbitGame::assignClientTeam(%game, %client) +{ + // all players start on team 1 + %client.team = $NonRabbitTeam; + + // set player's skin pref here + setTargetSkin(%client.target, %client.skin); + + if(%client.kills $= "") + %game.resetScore(%client); + + // Let everybody know you are no longer an observer: + messageAll( 'MsgClientJoinTeam', '\c1%1 has joined the hunt.', %client.name, "", %client, %client.team ); + updateCanListenState( %client ); +} + +function LakRabbitGame::playerSpawned(%game, %player) +{ + //call the default stuff first... + DefaultGame::playerSpawned(%game, %player); + + //find the rabbit + %clRabbit = -1; + for (%i = 0; %i < ClientGroup.getCount(); %i++) + { + %cl = ClientGroup.getObject(%i); + if (isObject(%cl.player) && isObject(%cl.player.holdingFlag)) + { + %clRabbit = %cl; + break; + } + } + + //now set a waypoint just for that client... + // ilys -- show client waypoint if not showing flag icon + if($Host::ShowFlagIcon == 0 && $Host::ShowFlagTask) + { + cancel(%player.client.waypointSchedule); + if (isObject(%clRabbit) && !%player.client.isAIControlled()) + %player.client.waypointSchedule = %game.showRabbitWaypointClient(%clRabbit, %player.client); + } + +// borlak -- start with favorites + if(!Game.PubPro) buyFavorites(%player.client); + + // Zeph - PubPro weapons + + else + { + %player.clearInventory(); + %player.setInventory(Disc,1); + %player.setInventory(Blaster,1); + %player.setInventory(Plasma,1); + %player.setInventory(DiscAmmo,30); + %player.setInventory(PlasmaAmmo,80); + %player.setInventory(Mine,6); + %player.setInventory(RepairKit,1); + %player.setInventory(EnergyPack,1); + %player.use("Disc"); + } + %player.schedule(250,"selectWeaponSlot", 0); + %player.setEnergyLevel(%player.getDatablock().maxEnergy); + + if($Host::EnableLakUnlimitedDJ == 1) + %player.freeDJ = 999; // free diskjump + else + %player.freeDJ = 1; // free diskjump + +} + + +// modified to spawn you near rabbit or flag +function LakRabbitGame::pickTeamSpawn(%game, %team) +{ + //find the rabbit + %spawnNear = -1; + for (%i = 0; %i < ClientGroup.getCount(); %i++) + { + %cl = ClientGroup.getObject(%i); + if (isObject(%cl.player) && isObject(%cl.player.holdingFlag)) + { + %spawnNear = %cl.player; + break; + } + } + + if(!isObject(%spawnNear)) + { + for (%i = 0; %i < Team0.getCount(); %i++) + { + %obj = Team0.getObject(%i); + + if(%obj.dataBlock $= Flag) + { + %spawnNear = %obj; + if(!%obj.isHome) + break; + } + } + } + + for(%i = 0; %i < 25; %i++) + { + %pos = %spawnNear.getWorldBoxCenter(); + %randx = getWord(%pos,0)+getRandom(-150,150); + %randy = getWord(%pos,1)+getRandom(-150,150); + %mask = $TypeMasks::StaticShapeObjectType | $TypeMasks::InteriorObjectType | $TypeMasks::TerrainObjectType; + %rayStart = %randx SPC %randy SPC "1000"; + %rayEnd = %randx SPC %randy SPC "-1000"; + %ground = ContainerRayCast(%rayStart, %rayEnd, %mask, 0); + if(%ground) + { + %loc = getWord(%ground,1) SPC getWord(%ground,2) SPC getWord(%ground,3); + if(isOutOfBounds(%loc)) + { + %ground = 0; + continue; + } + break; + } + } + if(%ground) + return %loc; + return "0 0 500"; +} + + +function LakRabbitGame::pickPlayerSpawn(%game, %client, %respawn) +{ +// borlak -- reset values at spawn + $LakDamaged[%client] = 0; + %client.duelSecondsCounted = 0; + %client.duelSeconds = 0; + %client.duelKills = 0; + %client.player.lastDamageType = 0; + %client.player.knockback = 0; + cancel(%client.duelTimer); + + // all spawns come from team 1 + return %game.pickTeamSpawn($NonRabbitTeam); +} + +function LakRabbitGame::createPlayer(%game, %client, %spawnLoc, %respawn) +{ + %client.team = $NonRabbitTeam; + DefaultGame::createPlayer(%game, %client, %spawnLoc, %respawn); +} + +function LakRabbitGame::recalcScore(%game, %client) +{ + //score is grabs + kills + (totalTime / 15 seconds); + %timeHoldingFlagMS = %client.flagTimeMS; + if (isObject(%client.player.holdingFlag)) + %timeHoldingFlagMS += getSimTime() - %client.startTime; + if(Game.PubPro) %client.score = %client.flagGrabs + (%client.kills*5) + %client.morepoints + mFloor(%timeHoldingFlagMS / 2000); + else %client.score = %client.flagGrabs + %client.kills + %client.morepoints + mFloor(%timeHoldingFlagMS / 2000); + messageClient(%client, 'MsgYourScoreIs', "", %client.score); + %game.recalcTeamRanks(%client); + %game.checkScoreLimit(%client); +} + + +function LakRabbitGame::onClientDamaged(%game, %clVictim, %clAttacker, %damageType, %sourceObject) +{ + //if the victim is the rabbit, and the attacker is not the rabbit, set the damage time... + if (isObject(%clAttacker) && %clAttacker != %clVictim) + { + if (%clVictim.team == $RabbitTeam) + %game.rabbitDamageTime = getSimTime(); + } + + //call the default + DefaultGame::onClientDamaged(%game, %clVictim, %clAttacker, %damageType, %sourceObject); +} + +function LakRabbitGame::onClientKilled(%game, %clVictim, %clKiller, %damageType, %implement, %damageLoc) +{ +// borlak - make sure victim isn't killer + if(%clKiller != %clVictim) + { + %clKiller.kills++; + %game.recalcScore(%clKiller); + + if($LakDamaged[%clVictim]) + %killer = $LakDamaged[%clVictim]; + else + %killer = %clKiller; + + if(Game.duelMode && %killer.player.holdingFlag && %damageType != $DamageType::Missile) + duelBonus(%killer); + } +// borlak -- flag bug fix + %clVictim.flagDeny = schedule(2500, 0, setFlagDeny, %clVictim, 0); + + DefaultGame::onClientKilled(%game, %clVictim, %clKiller, %damageType, %implement, %damageLoc); +} + +// z0dd - ZOD, 8/4/02: KineticPoet's flag updater code +function LakRabbitGame::updateFlagTransform(%game, %flag) +{ + %flag.setTransform(%flag.getTransform()); + %game.updateFlagThread[%flag] = %game.schedule(100, "updateFlagTransform", %flag); +} + +function LakRabbitGame::playerDroppedFlag(%game, %player) +{ + //set the flag status + %flag = %player.holdingFlag; + %player.holdingFlag = ""; + %game.updateFlagTransform(%flag); // z0dd - ZOD, 8/4/02, Call to KineticPoet's flag updater + %flag.carrier = ""; + $flagStatus = ""; + + %player.unmountImage($FlagSlot); + %flag.hide(false); + // ilys -- remove flag icon from player + if($Host::ShowFlagIcon == 1 || $Host::ShowFlagIcon == 2) + { + setTargetSensorGroup(%flag.getTarget(), $NonRabbitTeam); + %player.scopeWhenSensorVisible(false); + %target = %player.getTarget(); + setTargetRenderMask(%target, getTargetRenderMask(%target) & ~0x2); + setTargetAlwaysVisMask(%target, (1 << getTargetSensorGroup(%target))); + } + + if($Host::ShowFlagIcon == 1) + %flag.scopeWhenSensorVisible(true); + else if($Host::ShowFlagIcon == 2) + %flag.scopeWhenSensorVisible(false); + + // borlak -- throw the flag, don't just drop it like dead weight + // v3.1 -- in duel mode, make flag bounce up always, even if player isn't moving.. more midair grabs + %flag.static = false; + + %vec = (-1.0 + getRandom() * 2.0) SPC (-1.0 + getRandom() * 2.0) SPC getRandom(); + %vec = vectorScale(%vec, 10); + %dot = vectorDot("0 0 1",%eye); + if (%dot < 0) + %dot = -%dot; + %vec = vectorAdd(%vec,vectorScale("0 0 12",1 - %dot)); + %vec = vectorAdd(%vec,%player.getVelocity()); + if(%game.duelMode && getWord(%vec,2) < 35) + %vec = getWord(%vec, 0) @ " " @ getWord(%vec, 1) @ " 35"; + %pos = getBoxCenter(%player.getWorldBox()); + %vec = vectorScale(%vec, 75); + + %flag.setTransform(%pos); + %flag.applyImpulse(%pos,%vec); + %flag.setCollisionTimeout(%player); + + // ilys -- hide waypoint if not showing flag icon + if($Host::ShowFlagIcon == 0 && $Host::ShowFlagTask) + { + cancel(%game.waypointSchedule); + %game.hideRabbitWaypoint(%player.client); + } + + //set the client status + %player.client.flagTimeMS += getSimTime() - %player.client.startTime; + %player.client.team = $NonRabbitTeam; + %player.client.setSensorGroup($NonRabbitTeam); + setTargetSensorGroup(%player.getTarget(), $NonRabbitTeam); + + messageAllExcept(%player.client, -1, 'MsgRabbitFlagDropped', '\c2%1 dropped the flag!~wfx/misc/flag_drop.wav', %player.client.name); + // if the player left the mission area, he's already been notified + if(!%player.outArea) + messageClient(%player.client, 'MsgRabbitFlagDropped', '\c2You dropped the flag!~wfx/misc/flag_drop.wav'); + logEcho(%player.client.nameBase@" (pl "@%player@"/cl "@%player.client@") dropped flag"); + + %flag.returnThread = %game.schedule(%game.flagReturnTime, "returnFlag", %flag); + // z0dd - ZOD - SquirrelOfDeath, 10/02/02. Hack for flag collision bug. + %flag.searchSchedule = Game.schedule(10, "startFlagCollisionSearch", %flag); + +// borlak -- timer fix and re-catch fix + %player.client.flagDeny = schedule(2500, 0, setFlagDeny, %player.client, 0); + cancel(%player.client.duelTimer); + +// borlak -- give bonus for duel mode + if(%game.duelMode) + { + if(%player.lastDamageType == $DamageType::Suicide) + { + messageAll('MsgDuelBonus', '\c2%1 suicides and gets no points!', %player.client.name); + return; + } + + %points = 0; + for(%i = 1; %i <= %player.client.duelKills; %i++) + %points += (%i*4); + %kills = (%player.client.duelKills*12)+12; + if(%kills <= 0) %kills = 1; + %seconds = %player.client.duelSecondsCounted; + if(%seconds < %player.duelKills*4) + %seconds = %player.duelKills*4; + %points *= %kills / %seconds; + %points = mFloor(%points); + %plural = (%points != 1 ? 's' : ""); + %pluralP = (%player.client.duelKills != 1 ? 'people' : 'person'); + %pluralSec = (%player.client.duelSecondsCounted != 1 ? 's' : ""); + + if(%player.client.duelKills == 0) + messageAll('MsgDuelBonus', '\c2%1 kills nobody and gets nothing! Better luck next time....', %player.client.name, %points, %plural, %player.client.duelKills, %player.client.duelSecondsCounted); + else + messageAll('MsgDuelBonus', '\c2%1 gets %2 bonus point%3 for killing %4 %5 in %6 second%7.', %player.client.name, %points, %plural, %player.client.duelKills, %pluralP, %player.client.duelSecondsCounted, %pluralSec); + %player.client.morepoints += %points; + %game.recalcScore(%player.client); + } +} + +function LakRabbitGame::playerTouchFlag(%game, %player, %flag) +{ + if(%player.getState() $= "Dead" || %player.client.flagDeny) + return; + +// borlak - can't pick up flag until 2 ppl are on + if(PlayingPlayers() < 2) + { + //messageClient(%player.client, 'msgNoFlagWarning', "\c2You can't pick up the flag until another person joins." ); + return; + } + + // borlak -- flag capturing in CTF maps.. extra points.. rabbit mode only + if ((%flag.carrier $= "") && %player.holdingFlag && !%game.duelMode) + { + if(PlayingPlayers() < 6) + { + messageClient(%player.client, 'msgNoFlagWarning', "\c2You can't start capping until there are 6 players." ); + return; + } + %points = mFloor((getSimTime() - %player.client.startTime)/2000); + if(%points < 10) + %points = 10; // minimum 10 points + messageAll('MsgRabbitFlagTaken', '\c4%1 gets %2 points for a capturing the flag!~wfx/misc/flipflop_lost.wav', %player.client.name, %points); + %player.client.flagTimeMS += getSimTime() - %player.client.startTime; + %game.resetFlag(%player.holdingFlag); + cancel(%game.updateFlagThread[%flag]); // z0dd - ZOD, 8/4/02. Cancel this flag's thread to KineticPoet's flag updater + } + + // check if someone already has other flag.. only one flag can be in play.. also make sure you can only pick up flag that IS in play + %flagInPlay = 0; + for (%i = 0; %i < Team0.getCount(); %i++) + { + %obj = Team0.getObject(%i); + + if(%obj.dataBlock $= Flag) + { + if(!%obj.isHome) + %flagInPlay = %obj; + + if(!%obj.isHome && %obj.carrier) + { + messageClient(%player.client, 'msgNoFlagWarning', "\c2Only one flag may be in play."); + return; + } + } + } + + if(%flagInPlay && %flag != %flagInPlay) + { + messageClient(%player.client, 'msgNoFlagWarning', "\c2You can only pick up the flag in play."); + return; + } + + if(%flag.carrier $= "") + { +// borlak cancel flag search and remove free diskjump + cancel(%flag.searchSchedule); + %player.freeDJ = 0; + %flag.bounced = 0; + + %player.client.startTime = getSimTime(); + %player.holdingFlag = %flag; + %flag.carrier = %player; + %player.mountImage(FlagImage, $FlagSlot, true); //, $teamSkin[$RabbitTeam]); + cancel(%flag.returnThread); + %flag.hide(true); + // ilys -- add flag icon to player + if($Host::ShowFlagIcon == 1 || $Host::ShowFlagIcon == 2) + { + setTargetSensorGroup(%flag.getTarget(), $RabbitTeam); + %player.scopeWhenSensorVisible(true); + %target = %player.getTarget(); + setTargetRenderMask(%target, getTargetRenderMask(%target) | 0x2); + setTargetAlwaysVisMask(%target, 0x7); + } + %flag.isHome = false; + $flagStatus = %client.name; + +// borlak -- points for MA flag grabs + %mask = $TypeMasks::StaticShapeObjectType | $TypeMasks::InteriorObjectType | $TypeMasks::TerrainObjectType; + %rayStart = %player.getWorldBoxCenter(); + %rayEnd = getWord(%rayStart, 0) SPC getWord(%rayStart, 1) SPC getWord(%rayStart, 2) - 5; + %ground = ContainerRayCast(%rayStart, %rayEnd, %mask, 0); + if(!%ground) + { + %points = mFloor((getSpeed(%player)/5.3) + (getHeight(%player)/2.3)); + %points = %points > 5 ? %points : 5; + messageAll('MsgRabbitFlagTaken', '\c4%1 gets %2 points for a Mid-Air flag grab! [Speed:%3] [Height:%4]~wfx/misc/hunters_horde.wav', %player.client.name, %points, getSpeed(%player), getHeight(%player)); + %player.client.morepoints += %points; + } + else + messageAll('MsgRabbitFlagTaken', '\c2%1 has taken the flag!~wfx/misc/flag_snatch.wav', %player.client.name); + + logEcho(%player.client.nameBase@" (pl "@%player@"/cl "@%player.client@") took flag"); + %player.client.team = $RabbitTeam; + %player.client.setSensorGroup($RabbitTeam); + setTargetSensorGroup(%player.getTarget(), $RabbitTeam); + + //increase the score + %player.client.flagGrabs++; + %game.recalcScore(%player.client); + %game.schedule(5000, "RabbitFlagCheck", %player); + + //show the rabbit waypoint + %game.rabbitDamageTime = 0; + // ilys -- waypoint if not showing flag icon + if($Host::ShowFlagIcon == 0 && $Host::ShowFlagTask) + { + cancel(%game.waypointSchedule); + %game.showRabbitWaypoint(%player.client); + } + +// borlak - make rabbit invincible for 2 seconds .. + %player.setInvincible(true); + %player.schedule(2000, "setInvincible", false); + +// duel mode + if(%game.duelMode) + { + %player.client.duelTimer = schedule(1000, 0, checkDuelTimer, %player.client); + %player.client.duelSeconds = 36 - (PlayingPlayers()*4); + if(PlayingPlayers() == 2 || %game.noSplashDamage) + %player.client.duelSeconds += 10; + if(%player.client.duelSeconds < 20) + %player.client.duelSeconds = 20; + %player.client.duelKills = 0; + } + } +} + +function LakRabbitGame::rabbitFlagCheck(%game, %player) +{ + // this function calculates the score for the rabbit. It must be done periodically + // since the rabbit's score is based on how long the flag has been in possession. + if((%player.holdingFlag != 0) && (%player.getState() !$= "Dead")) + { + %game.recalcScore(%player.client); + //reschedule this flagcheck for 5 seconds + %game.schedule(5000, "RabbitFlagCheck", %player); + } +} + +function LakRabbitGame::returnFlag(%game, %flag) +{ + messageAll('MsgRabbitFlagReturned', '\c2The flag was returned to its starting point.~wfx/misc/flag_return.wav'); + logEcho("flag return (timeout)"); + %game.resetFlag(%flag); +} + +function LakRabbitGame::resetFlag(%game, %flag) +{ + %flag.setVelocity("0 0 0"); + %flag.setTransform(%flag.originalPosition); + %flag.isHome = true; + %flag.carrier = ""; + $flagStatus = ""; + %flag.hide(false); + + cancel(%flag.searchSchedule); + cancel(%game.updateFlagThread[%flag]); // z0dd - ZOD, 8/4/02. Cancel this flag's thread to KineticPoet's flag updater +} + +// ----- These functions are native to Rabbit + +function LakRabbitGame::timeLimitReached(%game) +{ + logEcho("game over (timelimit)"); + %game.gameOver(); + cycleMissions(); +} + +function LakRabbitGame::scoreLimitReached(%game) +{ + logEcho("game over (scorelimit)"); + %game.gameOver(); + cycleMissions(); +} + +function LakRabbitGame::checkScoreLimit(%game, %client) +{ + %scoreLimit = MissionGroup.Rabbit_scoreLimit; + // default of 1200 if scoreLimit not defined (that's 1200 seconds worth - 20 minutes) + if(%scoreLimit $= "") + %scoreLimit = 2000; + if(%client.score >= %scoreLimit) + %game.scoreLimitReached(); +} + +function LakRabbitGame::gameOver(%game) +{ + // z0dd - ZOD, 9/28/02. Hack for flag collision bug. + for(%f = 1; %f <= %game.numTeams; %f++) + { + cancel($TeamFlag[%f].searchSchedule); + } + + //call the default + DefaultGame::gameOver(%game); + + //send the message + messageAll('MsgGameOver', "Match has ended.~wvoice/announcer/ann.gameover.wav" ); + + cancel(%game.rabbitWaypointThread); + messageAll('MsgClearObjHud', ""); + for(%i = 0; %i < ClientGroup.getCount(); %i++) + { + %client = ClientGroup.getObject(%i); + %game.resetScore(%client); + // ilys -- cancel waypoint if not showing flag icon + if($Host::ShowFlagIcon == 0 && $Host::ShowFlagTask) + cancel(%client.waypointSchedule); + cancel(%client.duelTimer); + } + + // ilys -- cancel waypoint if not showing flag icon + if($Host::ShowFlagIcon == 0 && $Host::ShowFlagTask) + cancel(%game.waypointSchedule); + +// borlak -- delete variables + deleteVariables("$LakFired*"); + deleteVariables("$LakDamaged*"); +} + +function LakRabbitGame::resetScore(%game, %client) +{ + %client.score = 0; + %client.kills = 0; + %client.deaths = 0; + %client.suicides = 0; + %client.flagGrabs = 0; + %client.flagTimeMS = 0; + %client.morepoints = 0; + + // new debriefing stuff + %client.mas = 0; + %client.totalSpeed = 0; + %client.totalDistance = 0; + %client.totalChainAccuracy = 0; + %client.totalChainHits = 0; + %client.totalSnipeHits = 0; + %client.totalSnipes = 0; + %client.totalShockHits = 0; + %client.totalShocks = 0; +} + +function LakRabbitGame::enterMissionArea(%game, %playerData, %player) +{ + %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"); + cancel(%player.alertThread); +} + + +// borlak -- TAKEN FROM TR2 -- thanks! :D +function plzBounceOffGrid(%obj, %bounceForce, %count) +{ + %bounds = MissionArea.area; + %boundsWest = firstWord(%bounds); + %boundsNorth = getWord(%bounds, 1); + %boundsEast = %boundsWest + getWord(%bounds, 2); + %boundsSouth = %boundsNorth + getWord(%bounds, 3); + + %shapePos = %obj.getPosition(); + %shapex = firstWord(%shapePos); + %shapey = getWord(%shapePos, 1); + + if( %shapex >= %boundsWest && %shapex <= %boundsEast && %shapey >= %boundsNorth && %shapey <= %boundsSouth) { + // we don't need to bounce at all + return; + } + + if( %count == 8 ) { + // just kill this retard + %obj.scriptKill($DamageType::OutOfBounds); + return; + } + + if (%bounceForce $= "") + %bounceForce = 65; + + %oldVel = %obj.getVelocity(); + %obj.setVelocity("0 0 0"); + + %vecx = firstWord(%oldVel); + %vecy = getWord(%oldVel, 1); + %vecz = getWord(%oldVel, 2); + + // four cases, not two cases you fucktard kineticpoet + // no wonder the trives of vengrances failed + if(%shapex <= %boundsWest) { + %vecx = mAbs(%vecx); + } + else if(%shapex >= %boundsEast) { + %vecx = -mAbs(%vecx); + } + + if(%shapey <= %boundsNorth) { + %vecy = mAbs(%vecy); + } + else if(%shapey >= %boundsSouth) { + %vecy = -mAbs(%vecy); + } + + %vec = %vecx SPC %vecy SPC %vecz; + + // If the object's speed was pretty slow, give it a boost + %oldSpeed = VectorLen(%oldVel); + if (%oldSpeed < 25) + { + %vec = VectorNormalize(%vec); + %vec = VectorScale(%vec, 25); + } + else + %vec = VectorScale(%vec, 1.15); + + // apply the impulse to the object + //%obj.applyImpulse(%obj.getWorldBoxCenter(), %vec); + %obj.setVelocity(%vec); + + // repeat this bounce 4 times per second. if we're oob for 2 seconds, take action + // don't do this with the flag because that has its own thread + if( %obj.dataBlock !$= "Flag" ) { + schedule(250, 0, plzBounceOffGrid, %obj, %bounceForce, %count + 1); + } +} +function isOutOfBounds(%position) +{ + %shapePos = %position; + %shapex = firstWord(%shapePos); + %shapey = getWord(%shapePos, 1); + %bounds = MissionArea.area; + %boundsWest = firstWord(%bounds); + %boundsNorth = getWord(%bounds, 1); + %boundsEast = %boundsWest + getWord(%bounds, 2); + %boundsSouth = %boundsNorth + getWord(%bounds, 3); + + return (%shapex < %boundsWest || %shapex > %boundsEast || + %shapey < %boundsNorth || %shapey > %boundsSouth); +} +function getHeight(%this) +{ + %z = getWord(%this.getPosition(), 2); + return mFloor((%z - getTerrainHeight(%this.getPosition()))); +} + +function LakRabbitGame::leaveMissionArea(%game, %playerData, %player) +{ + plzBounceOffGrid(%player, 65); +} + +// z0dd - ZOD, 10/02/02. Hack for flag collision bug. +// borlak -- stolen from classic +function LakRabbitGame::startFlagCollisionSearch(%game, %flag) +{ + if(isOutOfBounds(%flag.getPosition())) + { + plzBounceOffGrid(%flag, 15); + %flag.bounced++; + if(%flag.bounced >= 25) + { + cancel(%flag.returnThread); + %game.returnFlag(%flag); + } + } + + %flag.searchSchedule = %game.schedule(10, "startFlagCollisionSearch", %flag); // SquirrelOfDeath, 10/02/02. Moved from after the while loop + %pos = %flag.getWorldBoxCenter(); + InitContainerRadiusSearch( %pos, 1.0, $TypeMasks::VehicleObjectType | $TypeMasks::CorpseObjectType | $TypeMasks::PlayerObjectType ); + while((%found = containerSearchNext()) != 0) + { + %flag.getDataBlock().onCollision(%flag, %found); + // SquirrelOfDeath, 10/02/02. Removed break to catch all players possibly intersecting with flag + } +} + +function LakRabbitGame::dropFlag(%game, %player) +{ + //you can no longer throw the flag in Rabbit... +} + +function LakRabbitGame::updateScoreHud(%game, %client, %tag) +{ + //tricky stuff here... use two columns if we have more than 15 clients... + %numClients = $TeamRank[0, count]; + if ( %numClients > $ScoreHudMaxVisible ) + %numColumns = 2; + + // Clear the header: + messageClient( %client, 'SetScoreHudHeader', "", "" ); + + // Send subheader: + if (%numColumns == 2) + messageClient(%client, 'SetScoreHudSubheader', "", '\tPLAYER\tSCORE\tTIME\tPLAYER\tSCORE\tTIME'); + else + messageClient(%client, 'SetScoreHudSubheader', "", '\tPLAYER\tSCORE\tTIME'); + + //recalc the score for whoever is holding the flag + if (isObject($AIRabbitFlag.carrier)) + %game.recalcScore($AIRabbitFlag.carrier.client); + + %countMax = %numClients; + if ( %countMax > ( 2 * $ScoreHudMaxVisible ) ) + { + if ( %countMax & 1 ) + %countMax++; + %countMax = %countMax / 2; + } + else if ( %countMax > $ScoreHudMaxVisible ) + %countMax = $ScoreHudMaxVisible; + + for (%index = 0; %index < %countMax; %index++) + { + //get the client info + %col1Client = $TeamRank[0, %index]; + %col1ClientScore = %col1Client.score $= "" ? 0 : %col1Client.score; + %col1Style = ""; + + if (isObject(%col1Client.player.holdingFlag)) + { + %col1ClientTimeMS = %col1Client.flagTimeMS + getSimTime() - %col1Client.startTime; + %col1Style = ""; + } + else + { + %col1ClientTimeMS = %col1Client.flagTimeMS; + if ( %col1Client == %client ) + %col1Style = ""; + } + + if (%col1ClientTimeMS <= 0) + %col1ClientTime = ""; + else + { + %minutes = mFloor(%col1ClientTimeMS / (60 * 1000)); + if (%minutes <= 0) + %minutes = "0"; + %seconds = mFloor(%col1ClientTimeMS / 1000) % 60; + if (%seconds < 10) + %seconds = "0" @ %seconds; + + %col1ClientTime = %minutes @ ":" @ %seconds; + } + + //see if we have two columns + if (%numColumns == 2) + { + %col2Client = ""; + %col2ClientScore = ""; + %col2ClientTime = ""; + %col2Style = ""; + + //get the column 2 client info + %col2Index = %index + %countMax; + if (%col2Index < %numClients) + { + %col2Client = $TeamRank[0, %col2Index]; + %col2ClientScore = %col2Client.score $= "" ? 0 : %col2Client.score; + + if (isObject(%col2Client.player.holdingFlag)) + { + %col2ClientTimeMS = %col2Client.flagTimeMS + getSimTime() - %col2Client.startTime; + %col2Style = ""; + } + else + { + %col2ClientTimeMS = %col2Client.flagTimeMS; + if ( %col2Client == %client ) + %col2Style = ""; + } + + if (%col2ClientTimeMS <= 0) + %col2ClientTime = ""; + else + { + %minutes = mFloor(%col2ClientTimeMS / (60 * 1000)); + if (%minutes <= 0) + %minutes = "0"; + %seconds = mFloor(%col2ClientTimeMS / 1000) % 60; + if (%seconds < 10) + %seconds = "0" @ %seconds; + + %col2ClientTime = %minutes @ ":" @ %seconds; + } + } + } + + //if the client is not an observer, send the message + if (%client.team != 0) + { + if ( %numColumns == 2 ) + messageClient( %client, 'SetLineHud', "", %tag, %index, '%7\t%1%2%3%8%4%5%6', + %col1Client.name, %col1ClientScore, %col1ClientTime, %col2Client.name, %col2ClientScore, %col2ClientTime, %col1Style, %col2Style ); + else + messageClient( %client, 'SetLineHud', "", %tag, %index, '%4\t%1%2%3', + %col1Client.name, %col1ClientScore, %col1ClientTime, %col1Style ); + } + //else for observers, create an anchor around the player name so they can be observed + else + { + if ( %numColumns == 2 ) + { + //this is really crappy, but I need to save 1 tag - can only pass in up to %9, %10 doesn't work... + if (%col2Style $= "") + { + messageClient( %client, 'SetLineHud', "", %tag, %index, '%7\t%1%2%3%4%5%6', + %col1Client.name, %col1ClientScore, %col1ClientTime, + %col2Client.name, %col2ClientScore, %col2ClientTime, + %col1Style, %col1Client, %col2Client ); + } + else if (%col2Style $= "") + { + messageClient( %client, 'SetLineHud', "", %tag, %index, '%7\t%1%2%3%4%5%6', + %col1Client.name, %col1ClientScore, %col1ClientTime, + %col2Client.name, %col2ClientScore, %col2ClientTime, + %col1Style, %col1Client, %col2Client ); + } + else + { + messageClient( %client, 'SetLineHud', "", %tag, %index, '%7\t%1%2%3%4%5%6', + %col1Client.name, %col1ClientScore, %col1ClientTime, + %col2Client.name, %col2ClientScore, %col2ClientTime, + %col1Style, %col1Client, %col2Client ); + } + } + else + messageClient( %client, 'SetLineHud', "", %tag, %index, '%4\t%1%2%3', + %col1Client.name, %col1ClientScore, %col1ClientTime, %col1Style, %col1Client ); + } + } + + // Tack on the list of observers: + %observerCount = 0; + for (%i = 0; %i < ClientGroup.getCount(); %i++) + { + %cl = ClientGroup.getObject(%i); + if (%cl.team == 0) + %observerCount++; + } + + if (%observerCount > 0) + { + messageClient( %client, 'SetLineHud', "", %tag, %index, ""); + %index++; + messageClient(%client, 'SetLineHud', "", %tag, %index, '\tOBSERVERS (%1)TIME', %observerCount); + %index++; + for (%i = 0; %i < ClientGroup.getCount(); %i++) + { + %cl = ClientGroup.getObject(%i); + //if this is an observer + if (%cl.team == 0) + { + %obsTime = getSimTime() - %cl.observerStartTime; + %obsTimeStr = %game.formatTime(%obsTime, false); + messageClient( %client, 'SetLineHud', "", %tag, %index, '\t%1%2', + %cl.name, %obsTimeStr ); + %index++; + } + } + } + + //clear the rest of Hud so we don't get old lines hanging around... + messageClient( %client, 'ClearHud', "", %tag, %index ); +} + +function LakRabbitGame::showRabbitWaypointClient(%game, %clRabbit, %client) +{ + //make sure we have a rabbit + if (!isObject(%clRabbit) || !isObject(%clRabbit.player) || !isObject(%clRabbit.player.holdingFlag)) + return; + + //no waypoints for bots + if (%client.isAIControlled()) + return; + + //scope the client, then set the always vis mask... + %clRabbit.player.scopeToClient(%client); + %visMask = getSensorGroupAlwaysVisMask(%clRabbit.getSensorGroup()); + %visMask |= (1 << %client.getSensorGroup()); + setSensorGroupAlwaysVisMask(%clRabbit.getSensorGroup(), %visMask); + + //now issue a command to kill the target + %client.setTargetId(%clRabbit.target); + commandToClient(%client, 'TaskInfo', %client, -1, false, "Kill the Rabbit!"); + %client.sendTargetTo(%client, true); + + //send the "waypoint is here sound" + messageClient(%client, 'MsgRabbitWaypoint', '~wfx/misc/target_waypoint.wav'); + + //and hide the waypoint + %client.waypointSchedule = %game.schedule(%game.waypointDuration, "hideRabbitWaypointClient", %clRabbit, %client); +} + +function LakRabbitGame::hideRabbitWaypointClient(%game, %clRabbit, %client) +{ + //no waypoints for bots + if (%client.isAIControlled()) + return; + + //unset the always vis mask... + %visMask = getSensorGroupAlwaysVisMask(%clRabbit.getSensorGroup()); + %visMask &= ~(1 << %client.getSensorGroup()); + setSensorGroupAlwaysVisMask(%clRabbit.getSensorGroup(), %visMask); + + //kill the actually task... + removeClientTargetType(%client, "AssignedTask"); +} + +function LakRabbitGame::showRabbitWaypoint(%game, %clRabbit) +{ + //make sure we have a rabbit + if (!isObject(%clRabbit) || !isObject(%clRabbit.player) || !isObject(%clRabbit.player.holdingFlag)) + return; + + //only show the rabbit waypoint if the rabbit hasn't been damaged within the frequency period + if (getSimTime() - %game.rabbitDamageTime < %game.waypointFrequency) + { + %game.waypointSchedule = %game.schedule(%game.waypointFrequency, "showRabbitWaypoint", %clRabbit); + return; + } + + //loop through all the clients and flash a waypoint at the rabbits position + for (%i = 0; %i < ClientGroup.getCount(); %i++) + { + %cl = ClientGroup.getObject(%i); + if (%cl.isAIControlled() || %cl == %clRabbit || !%cl.player) + continue; + +// borlak -- don't make waypoing if client is nearby (mess up aim) + %distance = VectorDist(%clRabbit.player.getWorldBoxCenter(), %cl.player.getWorldBoxCenter()); + if(%distance < 100) + continue; + + //scope the client, then set the always vis mask... + %clRabbit.player.scopeToClient(%cl); + %visMask = getSensorGroupAlwaysVisMask(%clRabbit.getSensorGroup()); + %visMask |= (1 << %cl.getSensorGroup()); + setSensorGroupAlwaysVisMask(%clRabbit.getSensorGroup(), %visMask); + + //now issue a command to kill the target + %cl.setTargetId(%clRabbit.target); + commandToClient(%cl, 'TaskInfo', %cl, -1, false, "Kill the Rabbit!"); + %cl.sendTargetTo(%cl, true); + + //send the "waypoint is here sound" + messageClient(%cl, 'MsgRabbitWaypoint', '~wfx/misc/target_waypoint.wav'); + } + + //schedule the time to hide the waypoint + %game.waypointSchedule = %game.schedule(%game.waypointDuration, "hideRabbitWaypoint", %clRabbit); +} + +function LakRabbitGame::hideRabbitWaypoint(%game, %clRabbit) +{ + //make sure we have a valid client + if (!isObject(%clRabbit)) + return; + + //loop through all the clients and hide the waypoint + for (%i = 0; %i < ClientGroup.getCount(); %i++) + { + %cl = ClientGroup.getObject(%i); + if (%cl.isAIControlled()) + continue; + + //unset the always vis mask... + %visMask = getSensorGroupAlwaysVisMask(%clRabbit.getSensorGroup()); + %visMask &= ~(1 << %cl.getSensorGroup()); + setSensorGroupAlwaysVisMask(%clRabbit.getSensorGroup(), %visMask); + + //kill the actually task... + removeClientTargetType(%cl, "AssignedTask"); + } + + //make sure we have a rabbit before scheduling the next showRabbitWaypoint... + if (isObject(%clRabbit.player) && isObject(%clRabbit.player.holdingFlag)) + %game.waypointSchedule = %game.schedule(%game.waypointFrequency, "showRabbitWaypoint", %clRabbit); +} + +function LakRabbitGame::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); + } + 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 LakRabbitGame::applyConcussion(%game, %player) +{ + // MES -- this won't do anything, the function LakRabbitGame::dropFlag is empty + %game.dropFlag( %player ); +} diff --git a/SCtF/DeleteObjects.cs b/SCtF/DeleteObjects.cs new file mode 100644 index 0000000..a613ac1 --- /dev/null +++ b/SCtF/DeleteObjects.cs @@ -0,0 +1,88 @@ +function getGroupObjectByName(%group, %name) +{ + %numObjects = %group.getCount(); + + for(%i = 0; %i < %numObjects; %i++) + { + if (%group.getObject(%i).getName() $= %name) + return %group.getObject(%i); + } +} + +function deleteObjectsFromMapByType(%type) +{ + %teamsGroup = getGroupObjectByName(MissionGroup, "Teams"); + if (!isObject(%teamsGroup)) + { + return; + } + + %team1Group = getGroupObjectByName(%teamsGroup, "Team1"); + if (!isObject(%team1Group)) + { + return; + } + + %team2Group = getGroupObjectByName(%teamsGroup, "Team2"); + if (!isObject(%team2Group)) + { + return; + } + + %team1Base0Group = getGroupObjectByName(%team1Group, "Base0"); + if (!isObject(%team1Base0Group)) + { + return; + } + + %team2Base0Group = getGroupObjectByName(%team2Group, "Base1"); + if (!isObject(%team2Base0Group)) + { + return; + } + + deleteObjectsFromGroupByType(%team1Base0Group, %type); + deleteObjectsFromGroupByType(%team2Base0Group, %type); +} + +function deleteObjectsFromGroupByType(%group, %type) +{ + %noObjectsLeft = 0; + + while (%noObjectsLeft == 0) + { + %i = 0; + %noObjectsLeft = 1; + %loop = 1; + %numObjects = %group.getCount(); + + while ((%loop == 1) && (%i < %numObjects)) + { + %obj = %group.getObject(%i); + + if (%obj.getClassName() $= "SimGroup") + deleteObjectsFromGroupByType(%obj, %type); + + if (%obj.getClassName() $= %type) + { + %obj.delete(); + %loop = 0; + %noObjectsLeft = 0; + } + else + %i++; + } + } +} + +function deleteNonSCtFObjectsFromMap() +{ + deleteObjectsFromGroupByType(MissionGroup, "Turret"); + deleteObjectsFromGroupByType(MissionGroup, "StaticShape"); + deleteObjectsFromGroupByType(MissionGroup, "TSStatic"); + deleteObjectsFromGroupByType(MissionGroup, "ForceFieldBare"); + deleteObjectsFromGroupByType(MissionGroup, "FlyingVehicle"); + deleteObjectsFromGroupByType(MissionGroup, "WheeledVehicle"); + deleteObjectsFromGroupByType(MissionGroup, "Waypoint"); +} + diff --git a/SCtFGame.cs b/SCtFGame.cs new file mode 100644 index 0000000..98a087f --- /dev/null +++ b/SCtFGame.cs @@ -0,0 +1,1997 @@ +// DisplayName = LCTF + +//--- 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 --- + +//-------------------------------------------------------------------------------- +// <> Spawn CTF <> +// +// Version: 1.1.25026 +// Date: August 09, 2003 +// By: ZOD +// http://www.planettribes.com/syrinx/ +// Note: For Classic mod only! +// +//-------------------------------------------------------------------------------- + +//exec the AI scripts +exec("scripts/aiSCtF.cs"); +//exec the prefs +exec("prefs/SctfPrefs.cs"); +//exec the DeleteObjects +exec("scripts/SCtF/DeleteObjects.cs"); + +// Setup the default bans +setArmorDefaults("Light"); +$Sctf::Armor = "Light"; + +//-- tracking --- +function SCtFGame::initGameVars(%game) +{ + %game.SCORE_PER_SUICIDE = 0; + %game.SCORE_PER_TEAMKILL = -10; + %game.SCORE_PER_DEATH = 0; + %game.SCORE_PER_TK_DESTROY = -10; + + %game.SCORE_PER_KILL = 10; + %game.SCORE_PER_PLYR_FLAG_CAP = 30; + %game.SCORE_PER_PLYR_FLAG_TOUCH = 20; + %game.SCORE_PER_TEAM_FLAG_CAP = 100; + %game.SCORE_PER_TEAM_FLAG_TOUCH = 1; + %game.SCORE_PER_ESCORT_ASSIST = 5; + %game.SCORE_PER_HEADSHOT = 1; + %game.SCORE_PER_REARSHOT = 1; + %game.SCORE_PER_MIDAIR = 1; + + %game.SCORE_PER_TURRET_KILL = 10; + %game.SCORE_PER_TURRET_KILL_AUTO = 5; + %game.SCORE_PER_FLAG_DEFEND = 5; + %game.SCORE_PER_CARRIER_KILL = 5; + %game.SCORE_PER_FLAG_RETURN = 10; + %game.SCORE_PER_STALEMATE_RETURN = 15; + %game.SCORE_PER_GEN_DEFEND = 5; + + %game.SCORE_PER_DESTROY_GEN = 10; + %game.SCORE_PER_DESTROY_SENSOR = 4; + %game.SCORE_PER_DESTROY_TURRET = 5; + %game.SCORE_PER_DESTROY_ISTATION = 2; + %game.SCORE_PER_DESTROY_VSTATION = 5; + %game.SCORE_PER_DESTROY_MPBTSTATION = 3; + %game.SCORE_PER_DESTROY_SOLAR = 5; + %game.SCORE_PER_DESTROY_SENTRY = 4; + %game.SCORE_PER_DESTROY_DEP_SENSOR = 1; + %game.SCORE_PER_DESTROY_DEP_INV = 2; + %game.SCORE_PER_DESTROY_DEP_TUR = 3; + + %game.SCORE_PER_DESTROY_SHRIKE = 5; + %game.SCORE_PER_DESTROY_BOMBER = 8; + %game.SCORE_PER_DESTROY_TRANSPORT = 5; + %game.SCORE_PER_DESTROY_WILDCAT = 5; + %game.SCORE_PER_DESTROY_TANK = 8; + %game.SCORE_PER_DESTROY_MPB = 12; + %game.SCORE_PER_PASSENGER = 2; + + %game.SCORE_PER_REPAIR_GEN = 8; + %game.SCORE_PER_REPAIR_SENSOR = 1; + %game.SCORE_PER_REPAIR_TURRET = 4; + %game.SCORE_PER_REPAIR_ISTATION = 2; + %game.SCORE_PER_REPAIR_VSTATION = 4; + %game.SCORE_PER_REPAIR_MPBTSTATION = 3; + %game.SCORE_PER_REPAIR_SOLAR = 4; + %game.SCORE_PER_REPAIR_SENTRY = 2; + %game.SCORE_PER_REPAIR_DEP_SEN = 1; + %game.SCORE_PER_REPAIR_DEP_TUR = 3; + %game.SCORE_PER_REPAIR_DEP_INV = 2; + + %game.FLAG_RETURN_DELAY = 45 * 1000; + + %game.TIME_CONSIDERED_FLAGCARRIER_THREAT = 3 * 1000; + %game.RADIUS_GEN_DEFENSE = 20; + %game.RADIUS_FLAG_DEFENSE = 20; + + %game.TOUCH_DELAY_MS = 20000; + + %game.fadeTimeMS = 2000; + + %game.notifyMineDist = 7.5; + + %game.stalemate = false; + %game.stalemateObjsVisible = false; + %game.stalemateTimeMS = 60000; + %game.stalemateFreqMS = 15000; + %game.stalemateDurationMS = 6000; + + %game.NotPure = false; +} + +package SCtFGame +{ + function ShapeBase::cleanNonType(%this, %type) + { + if(%type $= SCtF) + { + for(%h = 0; (%typeList = getWord(%this.missionTypesList, %h)) !$= ""; %h++) + if(%typeList $= CTF) + return; + } + Parent::cleanNonType(%this, %type); + } + + function ShapeBaseData::onDestroyed(%data, %obj, %prevstate) + { + %scorer = %obj.lastDamagedBy; + if(!isObject(%scorer)) + return; + + if((%scorer.getType() & $TypeMasks::GameBaseObjectType) && %scorer.getDataBlock().catagory $= "Vehicles") + { + %name = %scorer.getDatablock().getName(); + if(%name $= "BomberFlyer" || %name $= "AssaultVehicle") + %gunnerNode = 1; + else + %gunnerNode = 0; + + if(%scorer.getMountNodeObject(%gunnerNode)) + { + %destroyer = %scorer.getMountNodeObject(%gunnerNode).client; + %scorer = %destroyer; + %damagingTeam = %scorer.team; + } + } + else if(%scorer.getClassName() $= "Turret") + { + if(%scorer.getControllingClient()) + { + // manned turret + %destroyer = %scorer.getControllingClient(); + %scorer = %destroyer; + %damagingTeam = %scorer.team; + } + else + %scorer = %scorer.owner; // unmanned turret + } + if(!%damagingTeam) + %damagingTeam = %scorer.team; + + if(%damagingTeam != %obj.team) + { + if(!%obj.soiledByEnemyRepair) + Game.awardScoreStaticShapeDestroy(%scorer, %obj); + } + else + { + if(!%obj.getDataBlock().deployedObject) + Game.awardScoreTkDestroy(%scorer, %obj); + + return; + } + } + + function ShapeBaseData::onDisabled(%data, %obj) + { + %obj.wasDisabled = true; + Parent::onDisabled(%data, %obj); + } + + function RepairGunImage::onRepair(%this, %obj, %slot) + { + Parent::onRepair(%this, %obj, %slot); + %target = %obj.repairing; + if(%target && %target.team != %obj.team) + %target.soiledByEnemyRepair = true; + } + + function Flag::objectiveInit(%data, %flag) + { + if (!%flag.isTeamSkinned) + { + %pos = %flag.getTransform(); + %group = %flag.getGroup(); + } + %flag.originalPosition = %flag.getTransform(); + $flagPos[%flag.team] = %flag.originalPosition; + %flag.isHome = true; + %flag.carrier = ""; + %flag.grabber = ""; + setTargetSkin(%flag.getTarget(), CTFGame::getTeamSkin(CTFGame, %flag.team)); + setTargetSensorGroup(%flag.getTarget(), %flag.team); + setTargetAlwaysVisMask(%flag.getTarget(), 0x7); + setTargetRenderMask(%flag.getTarget(), getTargetRenderMask(%flag.getTarget()) | 0x2); + %flag.scopeWhenSensorVisible(true); + $flagStatus[%flag.team] = ""; + + //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" && %this.getClassName() !$= "TSStatic") + { + if(%this.getDataBlock().getName() $= "ExteriorFlagStand") + { + %flag.stand = %this; + %this.flag = %flag; + } + } + } + // set the nametag on the target + setTargetName(%flag.getTarget(), CTFGame::getTeamName(CTFGame, %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, CTFGame::getTeamName( CTFGame, %flag.team), "", "", 'Base', %flag.team, 0); + setTargetAlwaysVisMask(%target, 0xffffffff); + + //store the flag in an array + $TeamFlag[%flag.team] = %flag; + %flag.static = true; + + %flag.trigger = new Trigger() + { + dataBlock = flagTrigger; + polyhedron = "-0.6 0.6 0.1 1.2 0.0 0.0 0.0 -1.2 0.0 0.0 0.0 2.5"; + position = %flag.position; + rotation = %flag.rotation; + }; + MissionCleanup.add(%flag.trigger); + %flag.trigger.flag = %flag; + } + + function Flag::onEnterLiquid(%data, %obj, %coverage, %type) + { + if(%type > 3) // 1-3 are water, 4+ is lava and quicksand(?) + { + //error("flag("@%obj@") is in liquid type" SPC %type); + // Changed slightly so this can be cancelled if it leaves the + // lava before its supposed to be returned - Ilys + %obj.lavaEnterThread = Game.schedule(3000, "flagReturn", %obj); + } + } + + function Flag::onLeaveLiquid(%data, %obj, %type) + { + // Added to stop the flag retrun if it slides out of the lava - Ilys + if(isEventPending(%obj.lavaEnterThread)) + cancel(%obj.lavaEnterThread); + } + + function ProjectileData::onCollision(%data, %projectile, %targetObject, %modifier, %position, %normal) + { + if(!isObject(%targetObject) && !isObject(%projectile.sourceObject)) + return; + if(!(%targetObject.getType() & ($TypeMasks::StaticTSObjectType | $TypeMasks::InteriorObjectType | + $TypeMasks::TerrainObjectType | $TypeMasks::WaterObjectType))) + { + if(%projectile.sourceObject.team !$= %targetObject.team) + { + if(%targetObject.getDataBlock().getClassName() $= "PlayerData" && %data.getName() $= "DiscProjectile") + { + %mask = $TypeMasks::StaticShapeObjectType | $TypeMasks::InteriorObjectType | $TypeMasks::TerrainObjectType; + %start = %targetObject.getWorldBoxCenter(); + %distance = mFloor(VectorDist(%start, %projectile.initialPosition)); + %end = getWord(%start, 0) SPC getWord(%start, 1) SPC getWord(%start, 2) - 15; + %grounded = ContainerRayCast(%start, %end, %mask, 0); + if(!%grounded) + { + %projectile.sourceObject.client.scoreMidAir++; + messageClient(%projectile.sourceObject.client, 'MsgMidAir', '\c0You received a %1 point bonus for a successful mid air shot.', Game.SCORE_PER_MIDAIR, %data.radiusDamageType, %distance); + messageTeamExcept(%projectile.sourceObject.client, 'MsgMidAir', '\c5%1 hit a mid air shot.', %projectile.sourceObject.client.name, %data.radiusDamageType, %distance); + Game.recalcScore(%projectile.sourceObject.client); + } + } + } + Parent::onCollision(%data, %projectile, %targetObject, %modifier, %position, %normal); + } + } +}; + +///////////////////////////////////////////////////////////////////////////////////////// +// Mission Functions //////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////// + +function SCtFGame::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; + + echo( "starting camp thread..." ); + %game.campThread_1 = schedule( 1000, 0, "checkVehicleCamping", 1 ); + %game.campThread_2 = schedule( 1000, 0, "checkVehicleCamping", 2 ); +} + +function SCtFGame::clientMissionDropReady(%game, %client) +{ + %class = "CTFGame"; // Fake out clients objective hud into thinking this is CTF + messageClient(%client, 'MsgClientReady',"", %class); + %game.resetScore(%client); + for(%i = 1; %i <= %game.numTeams; %i++) + { + $Teams[%i].score = 0; + messageClient(%client, 'MsgCTFAddTeam', "", %i, %game.getTeamName(%i), $flagStatus[%i], $TeamScore[%i]); + } + messageClient(%client, 'MsgMissionDropInfo', '\c0You are in mission %1 (%2).', $MissionDisplayName, $MissionTypeDisplayName, $ServerName ); + DefaultGame::clientMissionDropReady(%game, %client); +} + +function SCtFGame::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 SCtFGame::equip(%game, %player) +{ + for(%i = 0; %i < $InventoryHudCount; %i++) + %player.client.setInventoryHudItem($InventoryHudData[%i, itemDataName], 0, 1); + + %player.client.clearBackpackIcon(); + if(!%player.client.isAIControlled()) + { + %player.setArmor($Sctf::Armor); + buyDeployableFavorites(%player.client); + %player.setEnergyLevel(%player.getDataBlock().maxEnergy); + %player.selectWeaponSlot( 0 ); + } + else + { + %player.setInventory(EnergyPack, 1); + %player.setInventory(Chaingun, 1); + %player.setInventory(ChaingunAmmo, %player.getDataBlock().max[ChaingunAmmo]); + %player.setInventory(Disc, 1); + %player.setInventory(DiscAmmo, %player.getDataBlock().max[DiscAmmo]); + %player.setInventory(GrenadeLauncher, 1); + %player.setInventory(GrenadeLauncherAmmo, %player.getDataBlock().max[GrenadeLauncherAmmo]); + %player.setInventory(Grenade, %player.getDataBlock().max[Grenade]); + %player.setInventory(Mine, %player.getDataBlock().max[Mine]); + %player.setInventory(Beacon, %player.getDataBlock().max[Beacon]); + %player.setInventory(RepairKit, 1); + %player.setInventory(TargetingLaser, 1); + %player.use("Disc"); + } + %player.weaponCount = 3; +} + +function SCtFGame::timeLimitReached(%game) +{ + logEcho("game over (timelimit)"); + %game.gameOver(); + cycleMissions(); +} + +function SCtFGame::scoreLimitReached(%game) +{ + logEcho("game over (scorelimit)"); + %game.gameOver(); + cycleMissions(); +} + +function SCtFGame::gameOver(%game) +{ + // z0dd - ZOD, 5/27/03. Kill the anti-turtle schedule + if(%game.turtleSchedule !$= "") + { + cancel(%game.turtleSchedule); + %game.turtleSchedule = ""; + } + + // z0dd - ZOD, 9/28/02. Hack for flag collision bug. + for(%f = 1; %f <= %game.numTeams; %f++) + { + cancel($TeamFlag[%f].searchSchedule); + cancel($TeamFlag[%f].lavaEnterThread); // Kill Ilys lava schedule - ZOD + } + + // ------------------------------------------- + // z0dd - ZOD, 9/28/02. Cancel camp schedules. + if( Game.campThread_1 !$= "" ) + cancel(Game.campThread_1); + + if( Game.campThread_2 !$= "" ) + cancel(Game.campThread_2); + + //call the default + DefaultGame::gameOver(%game); + + //send the winner message + %winner = ""; + if ($teamScore[1] > $teamScore[2]) + %winner = %game.getTeamName(1); + else if ($teamScore[2] > $teamScore[1]) + %winner = %game.getTeamName(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 if (%winner $= 'Starwolf') + messageAll('MsgGameOver', "Match has ended.~wvoice/announcer/ann.swwin.wav" ); + else if (%winner $= 'Blood Eagle') + messageAll('MsgGameOver', "Match has ended.~wvoice/announcer/ann.bewin.wav" ); + else if (%winner $= 'Diamond Sword') + messageAll('MsgGameOver', "Match has ended.~wvoice/announcer/ann.dswin.wav" ); + else if (%winner $= 'Phoenix') + messageAll('MsgGameOver', "Match has ended.~wvoice/announcer/ann.pxwin.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; +} + + +///////////////////////////////////////////////////////////////////////////////////////// +// Flag Functions /////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////// + +function SCtFGame::playerTouchFlag(%game, %player, %flag) +{ + %client = %player.client; + if ((%flag.carrier $= "") && (%player.getState() !$= "Dead")) + { + // z0dd - ZOD, 5/07/04. Cancel the lava return. + if(isEventPending(%obj.lavaEnterThread)) + cancel(%obj.lavaEnterThread); + + //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 SCtFGame::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 SCtFGame::playerTouchEnemyFlag(%game, %player, %flag) +{ + // --------------------------------------------------------------- + // z0dd, ZOD - 9/27/02. Player must wait to grab after throwing it + if((%player.flagTossWait !$= "") && %player.flagTossWait) + return false; + + cancel(%flag.searchSchedule); // z0dd - ZOD, 9/28/02. Hack for flag collision bug. SquirrelOfDeath, 10/02/02: Moved from PlayerTouchFlag + + cancel(%game.updateFlagThread[%flag]); // z0dd - ZOD, 8/4/02. Cancel this flag's thread to KineticPoet's flag updater + %game.flagHeldTime[%flag] = getSimTime(); // z0dd - ZOD, 8/15/02. Store time player grabbed 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, %game.getTeamSkin(%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; + %teamName = %game.getTeamName(%flag.team); + messageTeamExcept(%client, 'MsgCTFFlagTaken', '\c2Teammate %1 took the %2 flag.~wfx/misc/flag_snatch.wav', %client.name, %teamName, %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, %client.nameBase); + messageClient(%client, 'MsgCTFFlagTaken', '\c2You took the %2 flag.~wfx/misc/flag_snatch.wav', %client.name, %teamName, %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 SCtFGame::playerGotFlagTarget(%game, %player) +{ + %player.scopeWhenSensorVisible(true); + %target = %player.getTarget(); + setTargetRenderMask(%target, getTargetRenderMask(%target) | 0x2); + if(%game.stalemateObjsVisible) + setTargetAlwaysVisMask(%target, 0x7); +} + +function SCtFGame::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))); +} + +//---------------------------------------------------------------------------------------- +// z0dd - ZOD, 8/4/02: KineticPoet's flag updater code +function SCtFGame::updateFlagTransform(%game, %flag) +{ + %flag.setTransform(%flag.getTransform()); + %game.updateFlagThread[%flag] = %game.schedule(100, "updateFlagTransform", %flag); +} + +function SCtFGame::playerDroppedFlag(%game, %player) +{ + %client = %player.client; + %flag = %player.holdingFlag; + %game.updateFlagTransform(%flag); // z0dd - ZOD, 8/4/02, Call to KineticPoet's flag updater + %held = %game.formatTime(getSimTime() - %game.flagHeldTime[%flag], false); // z0dd - ZOD, 8/15/02. How long did player hold flag? + + %game.playerLostFlagTarget(%player); + + %player.holdingFlag = ""; //player isn't holding a flag anymore + %flag.carrier = ""; //flag isn't held anymore + $flagStatus[%flag.team] = ""; + + %player.unMountImage($FlagSlot); + %flag.hide(false); //Does the throwItem function handle this? + + %teamName = %game.getTeamName(%flag.team); + messageTeamExcept(%client, 'MsgCTFFlagDropped', '\c2Teammate %1 dropped the %2 flag. (Held: %4)~wfx/misc/flag_drop.wav', %client.name, %teamName, %flag.team, %held); // z0dd - ZOD, 8/15/02. How long flag was held + messageTeam(%flag.team, 'MsgCTFFlagDropped', '\c2Your flag has been dropped by %1! (Held: %4)~wfx/misc/flag_drop.wav', %client.name, 0, %flag.team, %held); // z0dd - ZOD, 8/15/02. How long flag was held + messageTeam(0, 'MsgCTFFlagDropped', '\c2%1 dropped the %2 flag. (Held: %4)~wfx/misc/flag_drop.wav', %client.name, %teamName, %flag.team, %held); // z0dd - ZOD, 8/15/02. How long flag was held + if(!%player.client.outOfBounds) + messageClient(%client, 'MsgCTFFlagDropped', '\c2You dropped the %2 flag. (Held: %4)~wfx/misc/flag_drop.wav', %client.name, %teamName, %flag.team, %held); // z0dd - ZOD, 8/15/02. How long flag was held + // Yogi, 8/18/02. 3rd param changed 0 -> %client.name + logEcho(%client.nameBase@" (pl "@%player@"/cl "@%client@") dropped team "@%flag.team@" flag"@" (Held: "@%held@")"); + + //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 SCtFGame::flagCap(%game, %player) +{ + %client = %player.client; + %flag = %player.holdingFlag; + %flag.carrier = ""; + + %held = %game.formatTime(getSimTime() - %game.flagHeldTime[%flag], false); // z0dd - ZOD, 8/15/02. How long did player hold flag? + + %game.playerLostFlagTarget(%player); + //award points to player and team + %teamName = %game.getTeamName(%flag.team); + messageTeamExcept(%client, 'MsgCTFFlagCapped', '\c2%1 captured the %2 flag! (Held: %5)~wfx/misc/flag_capture.wav', %client.name, %teamName, %flag.team, %client.team, %held); + messageTeam(%flag.team, 'MsgCTFFlagCapped', '\c2Your flag was captured by %1. (Held: %5)~wfx/misc/flag_lost.wav', %client.name, 0, %flag.team, %client.team, %held); + messageTeam(0, 'MsgCTFFlagCapped', '\c2%1 captured the %2 flag! (Held: %5)~wfx/misc/flag_capture.wav', %client.name, %teamName, %flag.team, %client.team, %held); + messageClient(%client, 'MsgCTFFlagCapped', '\c2You captured the %2 flag! (Held: %5)~wfx/misc/flag_capture.wav', %client.name, %teamName, %flag.team, %client.team, %held); // Yogi, 8/18/02. 3rd param changed 0 -> %client.name + + logEcho(%client.nameBase@" (pl "@%player@"/cl "@%client@") capped team "@%client.team@" flag"@" (Held: "@%held@")"); + %player.holdingFlag = ""; //no longer holding it. + %player.unMountImage($FlagSlot); + %game.awardScoreFlagCap(%client, %flag); + %game.flagReset(%flag); + + //call the AI function + %game.AIflagCap(%player, %flag); + + //if this cap didn't end the game, play the announcer... + if ($missionRunning) + { + if (%game.getTeamName(%client.team) $= 'Inferno') + messageAll("", '~wvoice/announcer/ann.infscores.wav'); + else if (%game.getTeamName(%client.team) $= 'Storm') + messageAll("", '~wvoice/announcer/ann.stoscores.wav'); + else if (%game.getTeamName(%client.team) $= 'Phoenix') + messageAll("", '~wvoice/announcer/ann.pxscore.wav'); + else if (%game.getTeamName(%client.team) $= 'Blood Eagle') + messageAll("", '~wvoice/announcer/ann.bescore.wav'); + else if (%game.getTeamName(%client.team) $= 'Diamond Sword') + messageAll("", '~wvoice/announcer/ann.dsscore.wav'); + else if (%game.getTeamName(%client.team) $= 'Starwolf') + messageAll("", '~wvoice/announcer/ann.swscore.wav'); + } +} + +function SCtFGame::flagReturnFade(%game, %flag) +{ + $FlagReturnTimer[%flag] = %game.schedule(%game.fadeTimeMS, "flagReturn", %flag); + %flag.startFade(%game.fadeTimeMS, 0, true); +} + +function SCtFGame::flagReturn(%game, %flag, %player) +{ + cancel($FlagReturnTimer[%flag]); + $FlagReturnTimer[%flag] = ""; + + if(%flag.team == 1) + %otherTeam = 2; + else + %otherTeam = 1; + %teamName = %game.getTeamName(%flag.team); + 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); + messageTeam(0, 'MsgCTFFlagReturned', '\c2%1 returned the %2 flag.~wfx/misc/flag_return.wav', %client.name, %teamName, %flag.team); + messageClient(%client, 'MsgCTFFlagReturned', '\c2You returned your flag.~wfx/misc/flag_return.wav', %client.name, %teamName, %flag.team); // Yogi, 8/18/02. 3rd param changed 0 -> %client.name + logEcho(%client.nameBase@" (pl "@%player@"/cl "@%client@") returned team "@%flag.team@" flag"); + + // find out what type of return it is + // stalemate return? + // --------------------------------------------------- + // z0dd - ZOD, 9/29/02. Removed T2 demo code from here + if(%game.stalemate) + { + //error("Stalemate return!!!"); + %game.awardScoreStalemateReturn(%player.client); + } + // regular return + else + { + %enemyFlagDist = vectorDist($flagPos[%flag.team], $flagPos[%otherTeam]); + %dist = vectorDist(%flag.position, %flag.originalPosition); + + %rawRatio = %dist/%enemyFlagDist; + %ratio = %rawRatio < 1 ? %rawRatio : 1; + %percentage = mFloor( (%ratio) * 10 ) * 10; + %game.awardScoreFlagReturn(%player.client, %percentage); + } + } + else + { + //returned due to timer + messageTeam(%otherTeam, 'MsgCTFFlagReturned', '\c2The %2 flag was returned to base.~wfx/misc/flag_return.wav', 0, %teamName, %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); + logEcho("team "@%flag.team@" flag returned (timeout)"); + } + %game.flagReset(%flag); +} + +function SCtFGame::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 SCtFGame::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 SCtFGame::beginStalemate(%game) +{ + %game.stalemate = true; + %game.showStalemateTargets(); + // z0dd - ZOD, 5/27/03. Added anti-turtling, return flags after x minutes + if(!$Host::TournamentMode) + { + messageAll( 'MsgStalemate', '\c3Anti turtle initialized. Flags will be returned to bases in %1 minutes.', $Host::ClassicAntiTurtleTime); + %game.turtleSchedule = %game.schedule($Host::ClassicAntiTurtleTime * 60000, 'antiTurtle'); + } +} + +function SCtFGame::endStalemate(%game) +{ + %game.stalemate = false; + %game.hideStalemateTargets(); + cancel(%game.stalemateSchedule); +} + +// z0dd - ZOD, 5/27/03. Anti-turtle function +function CTFGame::antiTurtle(%game) +{ + if(isEventPending(%game.turtleSchedule)) + cancel(%game.turtleSchedule); + + for (%i = 1; %i <= 2; %i++) + { + Game.flagReturn($TeamFlag[%i]); + messageAll( 'MsgCTFFlagReturned', '\c3Both flags returned to bases to break stalemate.~wfx/misc/flag_return.wav', 0, 0, %i); + } +} + +function SCtFGame::flagReset(%game, %flag) +{ + cancel(%game.updateFlagThread[%flag]); // z0dd - ZOD, 8/4/02. Cancel this flag's thread to KineticPoet's flag updater + + //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 = ""; + %flag.grabber = ""; + $flagStatus[%flag.team] = ""; + %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); + + // -------------------------------------------------------- + // z0dd - ZOD, 5/26/02. Don't let flag hover over defenders + %flag.static = true; + + // -------------------------------------------------------------------------- + // z0dd - ZOD, 9/28/02. Hack for flag collision bug. + if(%flag.searchSchedule !$="") + { + cancel(%flag.searchSchedule); + } +} + +function SCtFGame::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 SCtFGame::onClientDamaged(%game, %clVictim, %clAttacker, %damageType, %implement, %damageLoc) +{ + if(%clVictim.headshot && %damageType == $DamageType::Laser && %clVictim.team != %clAttacker.team) + { + %clAttacker.scoreHeadshot++; + if (%game.SCORE_PER_HEADSHOT != 0) + { + messageClient(%clAttacker, 'msgHeadshot', '\c0You received a %1 point bonus for a successful headshot.', %game.SCORE_PER_HEADSHOT); + messageTeamExcept(%clAttacker, 'msgHeadshot', '\c5%1 hit a sniper rifle headshot.', %clAttacker.name); + } + %game.recalcScore(%clAttacker); + } + if(%clVictim.rearshot && %damageType == $DamageType::ShockLance && %clVictim.team != %clAttacker.team) + { + %clAttacker.scoreRearshot++; + if (%game.SCORE_PER_REARSHOT != 0) + { + messageClient(%clAttacker, 'msgRearshot', '\c0You received a %1 point bonus for a successful rearshot.', %game.SCORE_PER_REARSHOT); + messageTeamExcept(%clAttacker, 'msgRearshot', '\c5%1 hit a shocklance rearshot.', %clAttacker.name); + } + %game.recalcScore(%clAttacker); + } + //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 SCtFGame::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 + + %cl.escortAssists * %game.SCORE_PER_ESCORT_ASSIST + + %cl.teamKills * %game.SCORE_PER_TEAMKILL + + %cl.tkDestroys * %game.SCORE_PER_TK_DESTROY + + %cl.scoreHeadshot * %game.SCORE_PER_HEADSHOT + + %cl.scoreRearshot * %game.SCORE_PER_REARSHOT + + %cl.scoreMidAir * %game.SCORE_PER_MIDAIR + + %cl.flagCaps * %game.SCORE_PER_PLYR_FLAG_CAP + + %cl.flagGrabs * %game.SCORE_PER_PLYR_FLAG_TOUCH + + %cl.genDestroys * %game.SCORE_PER_DESTROY_GEN + + %cl.sensorDestroys * %game.SCORE_PER_DESTROY_SENSOR + + %cl.turretDestroys * %game.SCORE_PER_DESTROY_TURRET + + %cl.iStationDestroys * %game.SCORE_PER_DESTROY_ISTATION + + %cl.vstationDestroys * %game.SCORE_PER_DESTROY_VSTATION + + %cl.mpbtstationDestroys * %game.SCORE_PER_DESTROY_MPBTSTATION + + %cl.solarDestroys * %game.SCORE_PER_DESTROY_SOLAR + + %cl.sentryDestroys * %game.SCORE_PER_DESTROY_SENTRY + + %cl.depSensorDestroys * %game.SCORE_PER_DESTROY_DEP_SENSOR + + %cl.depTurretDestroys * %game.SCORE_PER_DESTROY_DEP_TUR + + %cl.depStationDestroys * %game.SCORE_PER_DESTROY_DEP_INV + + %cl.vehicleScore + %cl.vehicleBonus; + + %cl.defenseScore = %cl.genDefends * %game.SCORE_PER_GEN_DEFEND + + %cl.flagDefends * %game.SCORE_PER_FLAG_DEFEND + + %cl.carrierKills * %game.SCORE_PER_CARRIER_KILL + + %cl.escortAssists * %game.SCORE_PER_ESCORT_ASSIST + + %cl.turretKills * %game.SCORE_PER_TURRET_KILL_AUTO + + %cl.mannedturretKills * %game.SCORE_PER_TURRET_KILL + + %cl.genRepairs * %game.SCORE_PER_REPAIR_GEN + + %cl.SensorRepairs * %game.SCORE_PER_REPAIR_SENSOR + + %cl.TurretRepairs * %game.SCORE_PER_REPAIR_TURRET + + %cl.StationRepairs * %game.SCORE_PER_REPAIR_ISTATION + + %cl.VStationRepairs * %game.SCORE_PER_REPAIR_VSTATION + + %cl.mpbtstationRepairs * %game.SCORE_PER_REPAIR_MPBTSTATION + + %cl.solarRepairs * %game.SCORE_PER_REPAIR_SOLAR + + %cl.sentryRepairs * %game.SCORE_PER_REPAIR_SENTRY + + %cl.depSensorRepairs * %game.SCORE_PER_REPAIR_DEP_SEN + + %cl.depInvRepairs * %game.SCORE_PER_REPAIR_DEP_INV + + %cl.depTurretRepairs * %game.SCORE_PER_REPAIR_DEP_TUR + + %cl.returnPts; + + %cl.score = mFloor(%cl.offenseScore + %cl.defenseScore); + %game.recalcTeamRanks(%cl); +} + +function SCtFGame::updateKillScores(%game, %clVictim, %clKiller, %damageType, %implement) +{ + // is this a vehicle kill rather than a player kill + // console error message suppression + if( isObject( %implement ) ) + { + if( %implement.getDataBlock().getName() $= "AssaultPlasmaTurret" || %implement.getDataBlock().getName() $= "BomberTurret" ) // gunner + %clKiller = %implement.vehicleMounted.getMountNodeObject(1).client; + else if(%implement.getDataBlock().catagory $= "Vehicles") // pilot + %clKiller = %implement.getMountNodeObject(0).client; + } + + 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 + { + %value = %game.awardScoreKill(%clKiller); + %game.shareScore(%clKiller, %value); + %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 SCtFGame::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 SCtFGame::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 SCtFGame::testCarrierKill(%game, %victimID, %killerID) +{ + %flag = %victimID.plyrDiedHoldingFlag; + return ((%flag !$= "") && (%flag.team == %killerID.team)); +} + +function SCtFGame::testEscortAssist(%game, %victimID, %killerID) +{ + return (%victimID.dmgdFlagCarrier); +} + +function SCtFGame::testValidRepair(%game, %obj) +{ + if(!%obj.wasDisabled) + { + //error(%obj SPC "was never disabled"); + return false; + } + else if(%obj.lastDamagedByTeam == %obj.team) + { + //error(%obj SPC "was last damaged by a friendly"); + return false; + } + else if(%obj.team != %obj.repairedBy.team) + { + //error(%obj SPC "was repaired by an enemy"); + return false; + } + else + { + if(%obj.soiledByEnemyRepair) + %obj.soiledByEnemyRepair = false; + return true; + } +} + +function SCtFGame::awardScoreFlagCap(%game, %cl, %flag) +{ + %cl.flagCaps++; + $TeamScore[%cl.team] += %game.SCORE_PER_TEAM_FLAG_CAP; + messageAll('MsgTeamScoreIs', "", %cl.team, $TeamScore[%cl.team]); + + %flag.grabber.flagGrabs++; + + if (%game.SCORE_PER_TEAM_FLAG_CAP > 0) + { + %plural = (%game.SCORE_PER_PLYR_FLAG_CAP != 1 ? 's' : ""); + %plural2 = (%game.SCORE_PER_PLYR_FLAG_TOUCH != 1 ? 's' : ""); + + if(%cl == %flag.grabber) + { + messageClient(%cl, 'msgCTFFriendCap', '\c0You receive %1 point%2 for stealing and capturing the enemy flag!', %game.SCORE_PER_PLYR_FLAG_CAP+%game.SCORE_PER_PLYR_FLAG_TOUCH, %plural); + messageTeam(%flag.team, 'msgCTFEnemyCap', '\c0Enemy %1 received %2 point%3 for capturing your flag!', %cl.name, %game.SCORE_PER_PLYR_FLAG_CAP+%game.SCORE_PER_PLYR_FLAG_TOUCH, %plural); + //messageTeamExcept(%cl, 'msgCTFFriendCap', '\c0Teammate %1 receives %2 point%3 for capturing the enemy flag!', %cl.name, %game.SCORE_PER_PLYR_FLAG_CAP+%game.SCORE_PER_PLYR_FLAG_TOUCH, %plural); // z0dd - ZOD, 8/15/02. Message is pointless + } + else + { + if(isObject(%flag.grabber)) // is the grabber still here? + { + messageClient(%cl, 'msgCTFFriendCap', '\c0You receive %1 point%2 for capturing the enemy flag! %3 gets %4 point%5 for the steal assist.', %game.SCORE_PER_PLYR_FLAG_CAP, %plural, %flag.grabber.name, %game.SCORE_PER_PLYR_FLAG_TOUCH, %plural2); + messageClient(%flag.grabber, 'msgCTFFriendCap', '\c0You receive %1 point%2 for stealing a flag that was subsequently capped by %3.', %game.SCORE_PER_PLYR_FLAG_TOUCH, %plural2, %cl.name); + } + else + messageClient(%cl, 'msgCTFFriendCap', '\c0You receive %1 point%2 for capturing the enemy flag!', %game.SCORE_PER_PLYR_FLAG_CAP, %plural); + } + } + %game.recalcScore(%cl); + if(isObject(%flag.grabber)) + %game.recalcScore(%flag.grabber); + + %game.checkScoreLimit(%cl.team); +} + + +function SCtFGame::awardScoreFlagTouch(%game, %cl, %flag) +{ + + %flag.grabber = %cl; + %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 %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 SCtFGame::resetDontScoreTimer(%game, %team) +{ + $dontScoreTimer[%team] = false; +} + +function SCtFGame::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 SCtFGame::awardScoreFlagReturn(%game, %cl, %perc) +{ + if (%game.SCORE_PER_FLAG_RETURN != 0) + { + %pts = mfloor( %game.SCORE_PER_FLAG_RETURN * (%perc/100) ); + if(%perc == 100) + messageClient(%cl, 'scoreFlaRetMsg', 'Flag return - exceeded capping distance - %1 point bonus.', %pts, %perc); + else if(%perc == 0) + messageClient(%cl, 'scoreFlaRetMsg', 'You gently place the flag back on the stand.', %pts, %perc); + else + messageClient(%cl, 'scoreFlaRetMsg', '\c0Flag return from %2%% of capping distance - %1 point bonus.', %pts, %perc); + %cl.returnPts += %pts; + } + %game.recalcScore(%cl); + return %game.SCORE_PER_FLAG_RETURN; +} + +function SCtFGame::awardScoreStalemateReturn(%game, %cl) +{ + if (%game.SCORE_PER_STALEMATE_RETURN != 0) + { + messageClient(%cl, 'scoreStaleRetMsg', '\c0You received a %1 point bonus for a stalemate-breaking, flag return.', %game.SCORE_PER_STALEMATE_RETURN); + %cl.returnPts += %game.SCORE_PER_STALEMATE_RETURN; + } + %game.recalcScore(%cl); + return %game.SCORE_PER_STALEMATE_RETURN; +} + +function SCtFGame::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', '\c2%1 defended our generator from an attack.', %killerID.name); // z0dd - ZOD, 8/15/02. Tell team + } + %game.recalcScore(%cl); + return %game.SCORE_PER_GEN_DEFEND; +} + +function SCtFGame::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', '\c2%1 stopped the enemy flag carrier.', %killerID.name); // z0dd - ZOD, 8/15/02. Tell team + } + %game.recalcScore(%killerID); + return %game.SCORE_PER_CARRIER_KILL; +} + +function SCtFGame::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', '\c2%1 defended our flag.', %killerID.name); // z0dd - ZOD, 8/15/02. Tell team + } + %game.recalcScore(%killerID); + return %game.SCORE_PER_FLAG_DEFEND; +} + +function SCtFGame::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', '\c2%1 protected our flag carrier.', %killerID.name); // z0dd - ZOD, 8/15/02. Tell team + } + %game.recalcScore(%killerID); + return %game.SCORE_PER_ESCORT_ASSIST; +} + +function SCtFGame::resetScore(%game, %client) +{ + %client.offenseScore = 0; + %client.kills = 0; + %client.scoreMidAir = 0; + %client.deaths = 0; + %client.suicides = 0; + %client.escortAssists = 0; + %client.teamKills = 0; + %client.tkDestroys = 0; // z0dd - ZOD, 10/03/02. Penalty for tking equiptment. + %client.flagCaps = 0; + %client.flagGrabs = 0; + %client.genDestroys = 0; + %client.sensorDestroys = 0; + %client.turretDestroys = 0; + %client.iStationDestroys = 0; + %client.vstationDestroys = 0; + %client.mpbtstationDestroys = 0; + %client.solarDestroys = 0; + %client.sentryDestroys = 0; + %client.depSensorDestroys = 0; + %client.depTurretDestroys = 0; + %client.depStationDestroys = 0; + %client.vehicleScore = 0; + %client.vehicleBonus = 0; + + %client.flagDefends = 0; + %client.defenseScore = 0; + %client.genDefends = 0; + %client.carrierKills = 0; + %client.escortAssists = 0; + %client.mannedTurretKills = 0; + %client.flagReturns = 0; + %client.genRepairs = 0; + %client.SensorRepairs = 0; + %client.TurretRepairs = 0; + %client.StationRepairs = 0; + %client.VStationRepairs = 0; + %client.mpbtstationRepairs = 0; + %client.solarRepairs = 0; + %client.sentryRepairs = 0; + %client.depSensorRepairs = 0; + %client.depInvRepairs = 0; + %client.depTurretRepairs = 0; + %client.returnPts = 0; + %client.score = 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// Asset Destruction scoring //////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////// + +function SCtFGame::awardScoreTkDestroy(%game, %cl, %obj) +{ + %cl.tkDestroys++; + messageTeamExcept(%cl, 'msgTkDes', '\c5Teammate %1 destroyed your team\'s %3 objective!', %cl.name, %game.cleanWord(%obj.getDataBlock().targetTypeTag)); + messageClient(%cl, 'msgTkDes', '\c0You have been penalized %1 points for destroying your teams equiptment.', %game.SCORE_PER_TK_DESTROY); + %game.recalcScore(%cl); + %game.shareScore(%cl, %game.SCORE_PER_TK_DESTROY); +} + +function SCtFGame::awardScoreStaticShapeDestroy(%game, %cl, %obj) +{ + %dataName = %obj.getDataBlock().getName(); + switch$ ( %dataName ) + { + case "GeneratorLarge": + %cl.genDestroys++; + %value = %game.SCORE_PER_DESTROY_GEN; + %msgType = 'msgGenDes'; + %tMsg = '\c5%1 destroyed an enemy %2 Generator!'; + %clMsg = '\c0You received a %1 point bonus for destroying an enemy generator.'; + + case "SolarPanel": + %cl.solarDestroys++; + %value = %game.SCORE_PER_DESTROY_SOLAR; + %msgType = 'msgSolarDes'; + %tMsg = '\c5%1 destroyed an enemy %2 Solar Panel!'; + %clMsg = '\c0You received a %1 point bonus for destroying an enemy solar panel.'; + + case "SensorLargePulse" or "SensorMediumPulse": + %cl.sensorDestroys++; + %value = %game.SCORE_PER_DESTROY_SENSOR; + %msgType = 'msgSensorDes'; + %tMsg = '\c5%1 destroyed an enemy %2 Sensor!'; + %clMsg = '\c0You received a %1 point bonus for destroying an enemy pulse sensor.'; + + case "TurretBaseLarge": + %cl.turretDestroys++; + %value = %game.SCORE_PER_DESTROY_TURRET; + %msgType = 'msgTurretDes'; + %tMsg = '\c5%1 destroyed an enemy %2 Turret!'; + %clMsg = '\c0You received a %1 point bonus for destroying an enemy base turret.'; + + case "StationInventory": + %cl.IStationDestroys++; + %value = %game.SCORE_PER_DESTROY_ISTATION; + %msgType = 'msgInvDes'; + %tMsg = '\c5%1 destroyed an enemy %2 Inventory Station!'; + %clMsg = '\c0You received a %1 point bonus for destroying an enemy inventory station.'; + + case "StationAmmo": + %cl.aStationDestroys++; + %value = %game.SCORE_PER_DESTROY_ASTATION; + %msgType = 'msgAmmoDes'; + %tMsg = '\c5%1 destroyed an enemy %2 Ammo Station!'; + %clMsg = '\c0You received a %1 point bonus for destroying an enemy ammo station.'; + + case "StationVehicle": + %cl.VStationDestroys++; + %value = %game.SCORE_PER_DESTROY_VSTATION; + %msgType = 'msgVSDes'; + %tMsg = '\c5%1 destroyed an enemy Vehicle Station!'; + %clMsg = '\c0You received a %1 point bonus for destroying an enemy vehicle station.'; + + case "SentryTurret": + %cl.sentryDestroys++; + %value = %game.SCORE_PER_DESTROY_SENTRY; + %msgType = 'msgSentryDes'; + %tMsg = '\c5%1 destroyed an enemy %2 Sentry Turret!'; + %clMsg = '\c0You received a %1 point bonus for destroying an enemy sentry turret.'; + + case "DeployedMotionSensor" or "DeployedPulseSensor": + %cl.depSensorDestroys++; + %value = %game.SCORE_PER_DESTROY_DEP_SENSOR; + %msgType = 'msgDepSensorDes'; + %tMsg = '\c5%1 destroyed an enemy Deployed Sensor!'; + %clMsg = '\c0You received a %1 point bonus for destroying an enemy deployed sensor.'; + + case "TurretDeployedWallIndoor" or "TurretDeployedFloorIndoor" or "TurretDeployedCeilingIndoor": + %cl.depTurretDestroys++; + %value = %game.SCORE_PER_DESTROY_DEP_TUR; + %msgType = 'msgDepTurDes'; + %tMsg = '\c5%1 destroyed an enemy Deployed Spider Clamp Turret!'; + %clMsg = '\c0You received a %1 point bonus for destroying an enemy deployed spider clamp turret.'; + + case "TurretDeployedOutdoor": + %cl.depTurretDestroys++; + %value = %game.SCORE_PER_DESTROY_DEP_TUR; + %msgType = 'msgDepTurDes'; + %tMsg = '\c5%1 destroyed an enemy Deployed Landspike Turret!'; + %clMsg = '\c0You received a %1 point bonus for destroying an enemy deployed landspike turret.'; + + case "DeployedStationInventory": + %cl.depStationDestroys++; + %value = %game.SCORE_PER_DESTROY_DEP_INV; + %msgType = 'msgDepInvDes'; + %tMsg = '\c5%1 destroyed an enemy Deployed Station!'; + %clMsg = '\c0You received a %1 point bonus for destroying an enemy deployed station.'; + + case "MPBTeleporter": + %cl.TStationDestroys++; + %value = %game.SCORE_PER_DESTROY_TSTATION; + %msgType = 'msgMPBTeleDes'; + %tMsg = '\c5%1 destroyed an enemy MPB Teleport Station!'; + %clMsg = '\c0You received a %1 point bonus for destroying an enemy MPB teleport station.'; + + default: + return; + } + teamDestroyMessage(%cl, 'MsgDestroyed', %tMsg, %cl.name, %obj.nameTag); + messageClient(%cl, %msgType, %clMsg, %value, %dataName); + %game.recalcScore(%cl); + %game.shareScore(%scorer, %value); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// Repair Scoring Functions ///////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////// + +function SCtFGame::testValidRepair(%game, %obj) +{ + if(!%obj.wasDisabled) + return false; + else if(%obj.lastDamagedByTeam == %obj.team) + return false; + else if(%obj.team != %obj.repairedBy.team) + return false; + else + { + if(%obj.soiledByEnemyRepair) + %obj.soiledByEnemyRepair = false; + + return true; + } +} + +function SCtFGame::objectRepaired(%game, %obj, %objName) +{ + %game.staticShapeOnRepaired(%obj, %objName); + %obj.wasDisabled = false; +} + +function SCtFGame::staticShapeOnRepaired(%game, %obj, %objName) +{ + if (%game.testValidRepair(%obj)) + { + %repairman = %obj.repairedBy; + %dataName = %obj.getDataBlock().getName(); + switch$ (%dataName) + { + case "GeneratorLarge": + %repairman.genRepairs++; + %score = %game.SCORE_PER_REPAIR_GEN; + %tMsgType = 'msgGenRepaired'; + %msgType = 'msgGenRep'; + %tMsg = '\c0%1 repaired the %2 Generator!'; + %clMsg = '\c0You received a %1 point bonus for repairing a generator.'; + + case "SolarPanel": + %repairman.solarRepairs++; + %score = %game.SCORE_PER_REPAIR_SOLAR; + %tMsgType = 'msgsolarRepaired'; + %msgType = 'msgsolarRep'; + %tMsg = '\c0%1 repaired the %2 Solar Panel!'; + %clMsg = '\c0You received a %1 point bonus for repairing a solar panel.'; + + case "SensorLargePulse" or "SensorMediumPulse": + %repairman.sensorRepairs++; + %score = %game.SCORE_PER_REPAIR_SENSOR; + %tMsgType = 'msgSensorRepaired'; + %msgType = 'msgSensorRep'; + %tMsg = '\c0%1 repaired the %2 Pulse Sensor!'; + %clMsg = '\c0You received a %1 point bonus for repairing a pulse sensor.'; + + case "StationInventory" or "StationAmmo": + %repairman.stationRepairs++; + %score = %game.SCORE_PER_REPAIR_ISTATION; + %tMsgType = 'msgStationRepaired'; + %msgType = 'msgIStationRep'; + %tMsg = '\c0%1 repaired the %2 Station!'; + %clMsg = '\c0You received a %1 point bonus for repairing a station.'; + + case "StationVehicle": + %repairman.VStationRepairs++; + %score = %game.SCORE_PER_REPAIR_VSTATION; + %tMsgType = 'msgvstationRepaired'; + %msgType = 'msgVStationRep'; + %tMsg = '\c0%1 repaired the Vehicle Station!'; + %clMsg = '\c0You received a %1 point bonus for repairing a vehicle station.'; + + case "TurretBaseLarge": + %repairman.TurretRepairs++; + %score = %game.SCORE_PER_REPAIR_TURRET; + %tMsgType = 'msgTurretRepaired'; + %msgType = 'msgTurretRep'; + %tMsg = '\c0%1 repaired the %2 Turret!'; + %clMsg = '\c0You received a %1 point bonus for repairing a base turret.'; + + case "SentryTurret": + %repairman.sentryRepairs++; + %score = %game.SCORE_PER_REPAIR_SENTRY; + %tMsgType = 'msgsentryTurretRepaired'; + %msgType = 'msgSentryRep'; + %tMsg = '\c0%1 repaired the %2 Sentry Turret!'; + %clMsg = '\c0You received a %1 point bonus for repairing a sentry turret.'; + + case "DeployedMotionSensor" or "DeployedPulseSensor": + %repairman.depSensorRepairs++; + %tMsgType = 'msgDepSensorRepaired'; + %msgType = 'msgDepSensorRep'; + %score = %game.SCORE_PER_REPAIR_DEP_SENSOR; + %tMsg = '\c0%1 repaired a Deployed Sensor!'; + %clMsg = '\c0You received a %1 point bonus for repairing a deployed sensor.'; + + case "TurretDeployedWallIndoor" or "TurretDeployedFloorIndoor" or "TurretDeployedCeilingIndoor": + %repairman.depTurretRepairs++; + %score = %game.SCORE_PER_REPAIR_DEP_TUR; + %tMsgType = 'msgDepTurretRepaired'; + %msgType = 'msgDepTurretRep'; + %tMsg = '\c0%1 repaired a Spider Clamp Turret!'; + %clMsg = '\c0You received a %1 point bonus for repairing a deployable spider clamp turret.'; + + case "TurretDeployedOutdoor": + %repairman.depTurretRepairs++; + %score = %game.SCORE_PER_REPAIR_DEP_TUR; + %tMsgType = 'msgDepTurretRepaired'; + %msgType = 'msgDepTurretRep'; + %tMsg = '\c0%1 repaired a Landspike Turret!'; + %clMsg = '\c0You received a %1 point bonus for repairing a deployable landspike turret.'; + + case "DeployedStationInventory": + %repairman.depInvRepairs++; + %score = %game.SCORE_PER_REPAIR_DEP_INV; + %tMsgType = 'msgDepInvRepaired'; + %msgType = 'msgDepInvRep'; + %tMsg = '\c0%1 repaired a Deployable Station!'; + %clMsg = '\c0You received a %1 point bonus for repairing a deployed station.'; + + case "MPBTeleporter": + %repairman.TStationRepairs++; + %score = %game.SCORE_PER_REPAIR_TSTATION; + %tMsgType = 'msgMPBTeleRepaired'; + %msgType = 'msgMPBTeleRep'; + %tMsg = '\c0%1 repaired the MPB Teleporter Station!'; + %clMsg = '\c0You received a %1 point bonus for repairing a mpb teleporter station.'; + + default: + return; + } + teamRepairMessage(%repairman, %tMsgType, %tMsg, %repairman.name, %obj.nameTag); + messageClient(%repairman, %msgType, %clMsg, %score, %dataName); + %game.recalcScore(%repairman); + } +} + +function SCtFGame::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 SCtFGame::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 SCtFGame::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; + + // ------------------------------------------------------------------------------ + // z0dd - ZOD - SquirrelOfDeath, 9/27/02. Delay on grabbing flag after tossing it + %player.flagTossWait = true; + %player.schedule(1000, resetFlagTossWait); + // ------------------------------------------------------------------------------ + + %client = %player.client; + %flag = %player.holdingFlag; + %flag.setVelocity("0 0 0"); + %flag.setTransform(%player.getWorldBoxCenter()); + %flag.setCollisionTimeout(%player); + + %held = %game.formatTime(getSimTime() - %game.flagHeldTime[%flag], false); // z0dd - ZOD, 8/15/02. How long did player hold flag? + + %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"); + + // z0dd - ZOD, 6/09/02. Remove anti-hover so flag can be thrown properly + %flag.static = false; + + // z0dd - ZOD, 10/02/02. Hack for flag collision bug. + %flag.searchSchedule = %game.schedule(10, "startFlagCollisionSearch", %flag); + + // 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); + + // z0dd - ZOD 3/30/02. Above message was sending the wrong varible to objective hud. + messageClient(%player.client, 'MsgCTFFlagDropped', '\c1You have left the mission area and lost the flag. (Held: %4)~wfx/misc/flag_drop.wav', %client.name, 0, %flag.team, %held); // Yogi, 8/18/02. 3rd param changed 0 -> %client.name + logEcho(%player.client.nameBase@" (pl "@%player@"/cl "@%player.client@") lost flag (out of bounds)"@" (Held: "@%held@")"); +} + +function SCtFGame::dropFlag(%game, %player) +{ + if(%player.holdingFlag > 0) + { + if (!%player.client.outOfBounds) + %player.throwObject(%player.holdingFlag); + else + %game.boundaryLoseFlag(%player); + } +} + +function SCtFGame::applyConcussion(%game, %player) +{ + %game.dropFlag( %player ); +} + +function SCtFGame::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); + + %enemyTeam = ( %destroyer.team == 1 ) ? 2 : 1; + + %scorer = 0; + %multiplier = 1; + + %passengers = 0; + for(%i = 0; %i < %data.numMountPoints; %i++) + if(%vehicle.getMountNodeObject(%i)) + %passengers++; + + //what destroyed this vehicle + if(%destroyer.client) + { + //it was a player, or his mine, satchel, whatever... + %destroyer = %destroyer.client; + %scorer = %destroyer; + + // determine if the object used was a mine + if(%vehicle.lastDamageType == $DamageType::Mine) + %multiplier = 2; + } + else if(%destroyer.getClassName() $= "Turret") + { + if(%destroyer.getControllingClient()) + { + //manned turret + %destroyer = %destroyer.getControllingClient(); + %scorer = %destroyer; + } + else + { + %destroyerName = "A turret"; + %multiplier = 0; + } + } + 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; + %scorer = %destroyer; + } + %multiplier = 3; + } + else // Is there anything else we care about? + return; + + + if(%destroyerName $= "") + %destroyerName = %destroyer.name; + + if(%vehicle.team == %destroyer.team) // team kill + { + %pref = (%vehicleType $= "Assault Tank") ? "an" : "a"; + messageAll( 'msgVehicleTeamDestroy', '\c0%1 TEAMKILLED %3 %2!', %destroyerName, %vehicleType, %pref); + } + + else // legit kill + { + //messageTeamExcept(%destroyer, 'msgVehicleDestroy', '\c0%1 destroyed an enemy %2.', %destroyerName, %vehicleType); // z0dd - ZOD, 8/20/02. not needed with new messenger on line below + teamDestroyMessage(%destroyer, 'msgVehDestroyed', '\c5%1 destroyed an enemy %2!', %destroyerName, %vehicleType); // z0dd - ZOD, 8/20/02. Send teammates a destroy message + messageTeam(%enemyTeam, 'msgVehicleDestroy', '\c0%1 destroyed your team\'s %2.', %destroyerName, %vehicleType); + //messageClient(%destroyer, 'msgVehicleDestroy', '\c0You destroyed an enemy %1.', %vehicleType); + + if(%scorer) + { + %value = %game.awardScoreVehicleDestroyed(%scorer, %vehicleType, %multiplier, %passengers); + %game.shareScore(%value); + } + } +} + +function SCtFGame::awardScoreVehicleDestroyed(%game, %client, %vehicleType, %mult, %passengers) +{ + // z0dd - ZOD, 9/29/02. Removed T2 demo code from here + + if(%vehicleType $= "Grav Cycle") + %base = %game.SCORE_PER_DESTROY_WILDCAT; + else if(%vehicleType $= "Assault Tank") + %base = %game.SCORE_PER_DESTROY_TANK; + else if(%vehicleType $= "MPB") + %base = %game.SCORE_PER_DESTROY_MPB; + else if(%vehicleType $= "Turbograv") + %base = %game.SCORE_PER_DESTROY_SHRIKE; + else if(%vehicleType $= "Bomber") + %base = %game.SCORE_PER_DESTROY_BOMBER; + else if(%vehicleType $= "Heavy Transport") + %base = %game.SCORE_PER_DESTROY_TRANSPORT; + + %total = ( %base * %mult ) + ( %passengers * %game.SCORE_PER_PASSENGER ); + + %client.vehicleScore += %total; + + messageClient(%client, 'msgVehicleScore', '\c0You received a %1 point bonus for destroying an enemy %2.', %total, %vehicleType); + %game.recalcScore(%client); + return %total; +} + +function SCtFGame::shareScore(%game, %client, %amount) +{ + // z0dd - ZOD, 9/29/02. Removed T2 demo code from here + + //error("share score of"SPC %amount SPC "from client:" SPC %client); + // all of the player in the bomber and tank share the points + // gained from any of the others + %vehicle = %client.vehicleMounted; + if(!%vehicle) + return 0; + + %vehicleType = getTaggedString(%vehicle.getDataBlock().targetTypeTag); + if(%vehicleType $= "Bomber" || %vehicleType $= "Assault Tank") + { + for(%i = 0; %i < %vehicle.getDataBlock().numMountPoints; %i++) + { + %occupant = %vehicle.getMountNodeObject(%i); + if(%occupant) + { + %occCl = %occupant.client; + if(%occCl != %client && %occCl.team == %client.team) + { + // the vehicle has a valid teammate at this node + // share the score with them + %occCl.vehicleBonus += %amount; + %game.recalcScore(%occCl); + } + } + } + } +} + +function SCtFGame::awardScoreTurretKill(%game, %victimID, %implement) +{ + if ((%killer = %implement.getControllingClient()) != 0) //award whoever might be controlling the turret + { + if (%killer == %victimID) + %game.awardScoreSuicide(%victimID); + else if (%killer.team == %victimID.team) //player controlling a turret killed a teammate + { + %killer.teamKills++; + %game.awardScoreTurretTeamKill(%victimID, %killer); + %game.awardScoreDeath(%victimID); + } + else + { + %killer.mannedturretKills++; + %game.recalcScore(%killer); + %game.awardScoreDeath(%victimID); + } + } + else if ((%killer = %implement.owner) != 0) //if it isn't controlled, award score to whoever deployed it + { + if (%killer.team == %victimID.team) + { + %game.awardScoreDeath(%victimID); + } + else + { + %killer.turretKills++; + %game.recalcScore(%killer); + %game.awardScoreDeath(%victimID); + } + } + //default is, no one was controlling it, no one owned it. No score given. +} + +function SCtFGame::testKill(%game, %victimID, %killerID) +{ + return ((%killerID !=0) && (%victimID.team != %killerID.team)); +} + +function SCtFGame::awardScoreKill(%game, %killerID) +{ + %killerID.kills++; + %game.recalcScore(%killerID); + return %game.SCORE_PER_KILL; +} + +function checkVehicleCamping( %team ) +{ + %position = $flagPos[%team]; + %radius = 5; + InitContainerRadiusSearch(%position, %radius, $TypeMasks::VehicleObjectType ); + + while ((%vehicle = containerSearchNext()) != 0) + { + %dist = containerSearchCurrRadDamageDist(); + + if (%dist > %radius) + continue; + else + { + //if( %vehicle.team == %team ) + applyVehicleCampDamage( %vehicle ); + } + } + + if( %team == 1 ) + Game.campThread_1 = schedule( 1000, 0, "checkVehicleCamping", 1 ); + else + Game.campThread_2 = schedule( 1000, 0, "checkVehicleCamping", 2 ); +} + +function applyVehicleCampDamage( %vehicle ) +{ + if( !isObject( %vehicle ) ) + return; + + if( %vehicle.getDamageState() $= "Destroyed" ) + return; + + %client = %vehicle.getMountNodeObject(0).client; // grab the pilot + + messageClient( %client, 'serverMessage', "Can't park vehicles in flag zones!" ); + %vehicle.getDataBlock().damageObject( %vehicle, 0, "0 0 0", 0.5, 0); +} + +// z0dd - ZOD, 10/02/02. Hack for flag collision bug. +function SCtFGame::startFlagCollisionSearch(%game, %flag) +{ + %flag.searchSchedule = %game.schedule(10, "startFlagCollisionSearch", %flag); // SquirrelOfDeath, 10/02/02. Moved from after the while loop + %pos = %flag.getWorldBoxCenter(); + InitContainerRadiusSearch( %pos, 1.0, $TypeMasks::VehicleObjectType | $TypeMasks::CorpseObjectType | $TypeMasks::PlayerObjectType ); + while((%found = containerSearchNext()) != 0) + { + %flag.getDataBlock().onCollision(%flag, %found); + // SquirrelOfDeath, 10/02/02. Removed break to catch all players possibly intersecting with flag + } +} + +///////////////////////////////////////////////////////////////////////////////////////// +// VOTING /////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////// + +//function SCtFGame::sendGameVoteMenu(%game, %client, %key) +//{ +// DefaultGame::sendGameVoteMenu(%game, %client, %key); +// if ( %game.scheduleVote $= "" ) +// { +// if(!%client.isAdmin) +// { +// messageClient( %client, 'MsgVoteItem', "", %key, 'VoteArmorClass', 'change the armor class to', 'Vote to change the Armor class' ); +// //messageClient( %client, 'MsgVoteItem', "", %key, 'VoteAntiTurtleTime', 'change the anti turtle time to', 'Vote Anti-Turtle time' ); +// } +// else +// { +// messageClient( %client, 'MsgVoteItem', "", %key, 'VoteArmorClass', 'change the armor class to', 'Change the Armor class' ); +// messageClient( %client, 'MsgVoteItem', "", %key, 'VoteAntiTurtleTime', 'change the anti turtle time to', 'Change Anti-Turtle time' ); +// } +// } +//} + +//function SCtFGame::sendAntiTurtleTimeList( %game, %client, %key ) +//{ +// messageClient( %client, 'MsgVoteItem', "", %key, 6, "", '6 minutes' ); +// messageClient( %client, 'MsgVoteItem', "", %key, 8, "", '8 minutes' ); +// messageClient( %client, 'MsgVoteItem', "", %key, 10, "", '10 minutes' ); +// messageClient( %client, 'MsgVoteItem', "", %key, 12, "", '12 minutes' ); +// messageClient( %client, 'MsgVoteItem', "", %key, 14, "", '14 minutes' ); +// messageClient( %client, 'MsgVoteItem', "", %key, 16, "", '16 minutes' ); +// messageClient( %client, 'MsgVoteItem', "", %key, 18, "", '18 minutes' ); +// messageClient( %client, 'MsgVoteItem', "", %key, 200, "", 'Disable Anti Turtle' ); +//} + +//function SCtFGame::sendArmorClassList(%game, %client, %key) +//{ +// messageClient( %client, 'MsgVoteItem', "", %key, "Light", "", 'Light Class' ); +// messageClient( %client, 'MsgVoteItem', "", %key, "Medium", "", 'Medium Class' ); +// messageClient( %client, 'MsgVoteItem', "", %key, "Heavy", "", 'Heavy Class' ); +//} + +//function serverCmdGetArmorClassList( %client, %key ) +//{ +// if ( isObject( Game ) ) +// Game.sendArmorClassList( %client, %key ); +//} + +//function SCtFGame::evalVote(%game, %typeName, %admin, %arg1, %arg2, %arg3, %arg4) +//{ +// DefaultGame::evalVote(%game, %typeName, %admin, %arg1, %arg2, %arg3, %arg4); +// switch$ (%typeName) +// { +// case "voteAntiTurtleTime": +// %game.voteAntiTurtleTime(%admin, %arg1, %arg2, %arg3, %arg4); +// +// case "VoteArmorClass": +// %game.VoteArmorClass(%admin, %arg1, %arg2, %arg3, %arg4); +// } +//} + +//function SCtFGame::voteAntiTurtleTime(%game, %admin, %newLimit) +//{ +// if( %newLimit == 200 ) +// %display = "disabled"; +// else +// %display = %newLimit; +// +// %cause = ""; +// if ( %admin ) +// { +// messageAll('MsgAdminForce', '\c3%1\c2 set the anti-turtle time to %2.~wfx/misc/diagnostic_on.wav', $AdminCl.name, %display); +// $Host::ClassicAntiTurtleTime = %newLimit; +// %cause = "(admin)"; +// } +// else +// { +// %totalVotes = %game.totalVotesFor + %game.totalVotesAgainst; +// if(%totalVotes > 0 && (%game.totalVotesFor / (ClientGroup.getCount() - $HostGameBotCount)) > ($Host::VotePasspercent / 100)) +// { +// messageAll('MsgVotePassed', '\c2The anti-turtle time is set to %1.', %display); +// $Host::ClassicAntiTurtleTime = %newLimit; +// %cause = "(vote)"; +// } +// else +// messageAll('MsgVoteFailed', '\c2The vote to change the anti-turtle time did not pass: %1 percent.', mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount) * 100)); +// } +// if(%cause !$= "") +// logEcho($AdminCl.name @ ": anti-turtle time set to "@%display SPC %cause, 1); +//} + +//function SCtFGame::VoteArmorClass(%game, %admin, %newLimit) +//{ +// %cause = ""; +// if ( %admin ) +// { +// messageAll('MsgAdminForce', '\c3%1\c2 set the armor class to %2.~wfx/misc/diagnostic_on.wav', $AdminCl.name, %newLimit); +// $Sctf::Armor = %newLimit; +// %cause = "(admin)"; +// } +// else +// { +// %totalVotes = %game.totalVotesFor + %game.totalVotesAgainst; +// if(%totalVotes > 0 && (%game.totalVotesFor / (ClientGroup.getCount() - $HostGameBotCount)) > ($Host::VotePasspercent / 100)) +// { +// messageAll('MsgVotePassed', '\c2The armor class was set to %1.', %newLimit); +// $Sctf::Armor = %newLimit; +// %cause = "(vote)"; +// } +// else +// messageAll('MsgVoteFailed', '\c2The vote to change the armor class did not pass: %1 percent.', mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount) * 100)); +// } +// switch$ ( %newLimit ) +// { +// case "Light": +// setArmorDefaults(%newLimit); +// +// case "Medium": +// setArmorDefaults(%newLimit); +// +// case "Heavy": +// setArmorDefaults(%newLimit); +// } +// if(%cause !$= "") +// logEcho($AdminCl.name @ ": armor class set to "@%display SPC %cause, 1); +//} + +//function serverCmdArmorDefaults(%client, %armor) +//{ +// if(%client.isAdmin) +// { +// Game.VoteArmorClass(true, %armor); +// } +//} + +function SCtFGame::missionLoadDone(%game) +{ + DefaultGame::missionLoadDone(%game); + deleteNonSCtFObjectsFromMap(); + $InvincibleTime = 5 + $Duel_StartInvincibleTime / 1000; +} diff --git a/admin.cs b/admin.cs new file mode 100644 index 0000000..1851688 --- /dev/null +++ b/admin.cs @@ -0,0 +1,591 @@ +// These have been secured against all those wanna-be-hackers. +$VoteMessage["VoteAdminPlayer"] = "Admin Player"; +$VoteMessage["VoteKickPlayer"] = "Kick Player"; +$VoteMessage["BanPlayer"] = "Ban Player"; +$VoteMessage["VoteChangeMission"] = "change the mission to"; +$VoteMessage["VoteTeamDamage", 0] = "enable team damage"; +$VoteMessage["VoteTeamDamage", 1] = "disable team damage"; +$VoteMessage["VoteTournamentMode"] = "change the server to"; +$VoteMessage["VoteFFAMode"] = "change the server to"; +$VoteMessage["VoteChangeTimeLimit"] = "change the time limit to"; +$VoteMessage["VoteMatchStart"] = "start the match"; +$VoteMessage["VoteGreedMode", 0] = "enable Hoard Mode"; +$VoteMessage["VoteGreedMode", 1] = "disable Hoard Mode"; +$VoteMessage["VoteHoardMode", 0] = "enable Greed Mode"; +$VoteMessage["VoteHoardMode", 1] = "disable Greed Mode"; +// z0dd - ZOD, 5/13/03. Added vote Random, Fair teams and armor limiting +$VoteMessage["VoteRandomTeams", 0] = "enable random teams"; +$VoteMessage["VoteRandomTeams", 1] = "disable random teams"; +$VoteMessage["VoteFairTeams", 0] = "enable fair teams"; +$VoteMessage["VoteFairTeams", 1] = "disable fair teams"; +$VoteMessage["VoteArmorLimits", 0] = "enable armor limiting"; +$VoteMessage["VoteArmorLimits", 1] = "disable armor limiting"; +$VoteMessage["VoteAntiTurtleTime"] = "change the anti turtle time to"; +$VoteMessage["VoteArmorClass"] = "change the armor class to"; +$VoteMessage["VoteClearServer"] = "clear server for match"; +$VoteMessage["VoteSkipMission"] = "skip the mission to"; + +function serverCmdStartNewVote(%client, %typeName, %arg1, %arg2, %arg3, %arg4, %playerVote) +{ + // z0dd - ZOD, 9/29/02. Removed T2 demo code from here + + // haha - who gets the last laugh... No admin for you! + if( %typeName $= "VoteAdminPlayer" && !$Host::allowAdminPlayerVotes ) + if( !%client.isSuperAdmin ) // z0dd - ZOD, 5/12/02. Allow Supers to do whatever the hell they want + return; + + %typePass = true; + + // if not a valid vote, turn back. + // z0dd - ZOD, 5/13/03. Added vote Random, Fair teams, armor limting, Anti-Turtle and Armor Class + if($VoteMessage[%typeName] $= "" && (%typeName !$= "VoteTeamDamage" && %typeName !$= "VoteHoardMode" + && %typeName !$= "VoteGreedMode" && %typeName !$= "VoteRandomTeams" + && %typeName !$= "VoteFairTeams" && %typeName !$= "VoteArmorLimits" + && %typeName !$= "VoteAntiTurtleTime" && %typeName !$= "VoteArmorClass" + && %typeName !$= "VoteClearServer" && %typeName !$= "VoteSkipMission")) { + %typePass = false; + } + + if(( $VoteMessage[ %typeName, $TeamDamage ] $= "" && %typeName $= "VoteTeamDamage" )) + %typePass = false; + + if( !%typePass ) + return; // -> bye ;) + + // ------------------------------------ + // z0dd - ZOD, 10/03/02. Fixed ban code + //if( %typeName $= "BanPlayer" ) + // if( !%client.isSuperAdmin ) + // return; // -> bye ;) + if( %typeName $= "BanPlayer" ) + { + if( !%client.isSuperAdmin ) + { + return; // -> bye ;) + } + else + { + ban( %arg1, %client ); + return; + } + } + // ------------------------------------ + + %isAdmin = ( %client.isAdmin || %client.isSuperAdmin ); + + // z0dd - ZOD, 5/19/03. Get the Admins client. + if(%isAdmin) + $AdminCl = %client; + + // keep these under the server's control. I win. + if( !%playerVote ) + %actionMsg = $VoteMessage[ %typeName ]; + else if( %typeName $= "VoteTeamDamage" || %typeName $= "VoteGreedMode" || %typeName $= "VoteHoardMode" ) + %actionMsg = $VoteMessage[ %typeName, $TeamDamage ]; + else + %actionMsg = $VoteMessage[ %typeName ]; + + if( !%client.canVote && !%isAdmin ) + return; + + if ( ( !%isAdmin || ( %arg1.isAdmin && ( %client != %arg1 ) ) ) && // z0dd - ZOD, 4/7/02. Allow SuperAdmins to kick Admins + !( ( %typeName $= "VoteKickPlayer" ) && %client.isSuperAdmin ) ) // z0dd - ZOD, 4/7/02. Allow SuperAdmins to kick Admins + { + %teamSpecific = false; + %gender = (%client.sex $= "Male" ? 'he' : 'she'); + if ( Game.scheduleVote $= "" ) + { + %clientsVoting = 0; + + //send a message to everyone about the vote... + if ( %playerVote ) + { + %teamSpecific = ( %typeName $= "VoteKickPlayer" ) && ( Game.numTeams > 1 ); + %kickerIsObs = %client.team == 0; + %kickeeIsObs = %arg1.team == 0; + %sameTeam = %client.team == %arg1.team; + + if( %kickeeIsObs ) + { + %teamSpecific = false; + %sameTeam = false; + } + if(( !%sameTeam && %teamSpecific) && %typeName !$= "VoteAdminPlayer") + { + messageClient(%client, '', '\c2Player votes must be team based.'); + return; + } + + // kicking is team specific + if( %typeName $= "VoteKickPlayer" ) + { + if(%arg1.isSuperAdmin) + { + messageClient(%client, '', '\c2You can not %1 %2, %3 is a Super Admin!', %actionMsg, %arg1.name, %gender); + return; + } + + Game.kickClient = %arg1; + Game.kickClientName = %arg1.name; + Game.kickGuid = %arg1.guid; + Game.kickTeam = %arg1.team; + + if(%teamSpecific) + { + for ( %idx = 0; %idx < ClientGroup.getCount(); %idx++ ) + { + %cl = ClientGroup.getObject( %idx ); + + if (%cl.isAdmin == true || (%cl.team == %client.team && !%cl.isAIControlled())) + { + if(%cl.isAdmin == true && %cl.team !$= %client.team) { + messageClient(%cl, 'AdminOtherTeamKickVoteStarted', '\c2%1 has initiated a vote to kick %2 on the \c3Other Team.~wgui/objective_notification.wav', %client.name, %arg1.name); + } + else + messageClient( %cl, 'VoteStarted', '\c2%1 initiated a vote to %2 %3.', %client.name, %actionMsg, %arg1.name); + %clientsVoting++; + } + } + } + else + { + for ( %idx = 0; %idx < ClientGroup.getCount(); %idx++ ) + { + %cl = ClientGroup.getObject( %idx ); + if ( !%cl.isAIControlled() ) + { + messageClient( %cl, 'VoteStarted', '\c2%1 initiated a vote to %2 %3.', %client.name, %actionMsg, %arg1.name); + %clientsVoting++; + } + } + } + } + else + { + for ( %idx = 0; %idx < ClientGroup.getCount(); %idx++ ) + { + %cl = ClientGroup.getObject( %idx ); + if ( !%cl.isAIControlled() ) + { + messageClient( %cl, 'VoteStarted', '\c2%1 initiated a vote to %2 %3.', %client.name, %actionMsg, %arg1.name); + %clientsVoting++; + } + } + } + } + else if ( %typeName $= "VoteChangeMission" ) + { + for ( %idx = 0; %idx < ClientGroup.getCount(); %idx++ ) + { + %cl = ClientGroup.getObject( %idx ); + if ( !%cl.isAIControlled() ) + { + messageClient( %cl, 'VoteStarted', '\c2%1 initiated a vote to %2 %3 (%4).', %client.name, %actionMsg, %arg1, %arg2 ); + %clientsVoting++; + } + } + } + else if (%arg1 !$= 0) + { + if (%arg2 !$= 0) + { + if(%typeName $= "VoteTournamentMode") + { + %admin = getAdmin(); + if(%admin > 0) + { + for ( %idx = 0; %idx < ClientGroup.getCount(); %idx++ ) + { + %cl = ClientGroup.getObject( %idx ); + if ( !%cl.isAIControlled() ) + { + messageClient( %cl, 'VoteStarted', '\c2%1 initiated a vote to %2 Tournament Mode (%3).', %client.name, %actionMsg, %arg1); + %clientsVoting++; + } + } + } + else + { + messageClient( %client, 'clientMsg', 'There must be a server admin to play in Tournament Mode.'); + return; + } + } + else + { + for ( %idx = 0; %idx < ClientGroup.getCount(); %idx++ ) + { + %cl = ClientGroup.getObject( %idx ); + if ( !%cl.isAIControlled() ) + { + messageClient( %cl, 'VoteStarted', '\c2%1 initiated a vote to %2 %3 %4.', %client.name, %actionMsg, %arg1, %arg2); + %clientsVoting++; + } + } + } + } + else + { + for ( %idx = 0; %idx < ClientGroup.getCount(); %idx++ ) + { + %cl = ClientGroup.getObject( %idx ); + if ( !%cl.isAIControlled() ) + { + messageClient( %cl, 'VoteStarted', '\c2%1 initiated a vote to %2 %3.', %client.name, %actionMsg, %arg1); + %clientsVoting++; + } + } + } + } + else + { + for ( %idx = 0; %idx < ClientGroup.getCount(); %idx++ ) + { + %cl = ClientGroup.getObject( %idx ); + if ( !%cl.isAIControlled() ) + { + messageClient( %cl, 'VoteStarted', '\c2%1 initiated a vote to %2.', %client.name, %actionMsg); + %clientsVoting++; + } + } + } + + // open the vote hud for all clients that will participate in this vote + if(%teamSpecific) + { + for ( %clientIndex = 0; %clientIndex < ClientGroup.getCount(); %clientIndex++ ) + { + %cl = ClientGroup.getObject( %clientIndex ); + + if(%cl.team == %client.team && !%cl.isAIControlled()) + messageClient(%cl, 'openVoteHud', "", %clientsVoting, ($Host::VotePassPercent / 100)); + } + } + else + { + for ( %clientIndex = 0; %clientIndex < ClientGroup.getCount(); %clientIndex++ ) + { + %cl = ClientGroup.getObject( %clientIndex ); + if ( !%cl.isAIControlled() ) + messageClient(%cl, 'openVoteHud', "", %clientsVoting, ($Host::VotePassPercent / 100)); + } + } + clearVotes(); + Game.voteType = %typeName; + Game.scheduleVote = schedule( ($Host::VoteTime * 1000), 0, "calcVotes", %typeName, %arg1, %arg2, %arg3, %arg4 ); + %client.vote = true; + messageAll('addYesVote', ""); + + if(!%client.team == 0) + clearBottomPrint(%client); + } + else + messageClient( %client, 'voteAlreadyRunning', '\c2A vote is already in progress.' ); + } + else + { + if ( Game.scheduleVote !$= "" && Game.voteType $= %typeName ) + { + messageAll('closeVoteHud', ""); + cancel(Game.scheduleVote); + Game.scheduleVote = ""; + } + + // if this is a superAdmin, don't kick or ban + if(%arg1 != %client) + { + if(!%arg1.isSuperAdmin) + { + // Set up kick/ban values: + if ( %typeName $= "VoteBanPlayer" || %typeName $= "VoteKickPlayer" ) + { + Game.kickClient = %arg1; + Game.kickClientName = %arg1.name; + Game.kickGuid = %arg1.guid; + Game.kickTeam = %arg1.team; + } + + //Tinman - PURE servers can't call "eval" + //Mark - True, but neither SHOULD a normal server + // - thanks Ian Hardingham + //if (!isPureServer()) + // eval( "Game." @ %typeName @ "(true,\"" @ %arg1 @ "\",\"" @ %arg2 @ "\",\"" @ %arg3 @ "\",\"" @ %arg4 @ "\");" ); + //else + Game.evalVote(%typeName, true, %arg1, %arg2, %arg3, %arg4); + } + else + messageClient(%client, '', '\c2You can not %1 %2, %3 is a Super Admin!', %actionMsg, %arg1.name, %gender); + } + } + %client.canVote = false; + %client.rescheduleVote = schedule( ($Host::voteSpread * 1000) + ($Host::voteTime * 1000) , 0, "resetVotePrivs", %client ); +} + +function resetVotePrivs( %client ) +{ + //messageClient( %client, '', 'You may now start a new vote.'); + %client.canVote = true; + %client.rescheduleVote = ""; +} + +function serverCmdSetPlayerVote(%client, %vote) +{ + // players can only vote once + if( %client.vote $= "" ) + { + %client.vote = %vote; + if(%client.vote == 1) + messageAll('addYesVote', ""); + else + messageAll('addNoVote', ""); + + commandToClient(%client, 'voteSubmitted', %vote); + } +} + +function calcVotes(%typeName, %arg1, %arg2, %arg3, %arg4) +{ + if(%typeName $= "voteMatchStart") + if($MatchStarted || $countdownStarted) + return; + + for ( %clientIndex = 0; %clientIndex < ClientGroup.getCount(); %clientIndex++ ) + { + %cl = ClientGroup.getObject( %clientIndex ); + messageClient(%cl, 'closeVoteHud', ""); + + if ( %cl.vote !$= "" ) + { + if ( %cl.vote ) + { + Game.votesFor[%cl.team]++; + Game.totalVotesFor++; + } + else + { + Game.votesAgainst[%cl.team]++; + Game.totalVotesAgainst++; + } + } + else + { + Game.votesNone[%cl.team]++; + Game.totalVotesNone++; + } + } + //Tinman - PURE servers can't call "eval" + //Mark - True, but neither SHOULD a normal server + // - thanks Ian Hardingham + //if (!isPureServer()) + // eval( "Game." @ %typeName @ "(false,\"" @ %arg1 @ "\",\"" @ %arg2 @ "\",\"" @ %arg3 @ "\",\"" @ %arg4 @ "\");" ); + //else + Game.evalVote(%typeName, false, %arg1, %arg2, %arg3, %arg4); + Game.scheduleVote = ""; + Game.kickClient = ""; + clearVotes(); +} + +function clearVotes() +{ + for(%clientIndex = 0; %clientIndex < ClientGroup.getCount(); %clientIndex++) + { + %client = ClientGroup.getObject(%clientIndex); + %client.vote = ""; + messageClient(%client, 'clearVoteHud', ""); + } + + for(%team = 1; %team < 5; %team++) + { + Game.votesFor[%team] = 0; + Game.votesAgainst[%team] = 0; + Game.votesNone[%team] = 0; + Game.totalVotesFor = 0; + Game.totalVotesAgainst = 0; + Game.totalVotesNone = 0; + } +} + +// Tournament mode Stuff----------------------------------- + +function setModeFFA( %mission, %missionType ) +{ + if( $Host::TournamentMode ) + { + $Host::TournamentMode = false; + + if( isObject( Game ) ) + Game.gameOver(); + + loadMission(%mission, %missionType, false); + } +} + +function setModeTournament( %mission, %missionType ) +{ + if( !$Host::TournamentMode ) + { + $Host::TournamentMode = true; + + if( isObject( Game ) ) + Game.gameOver(); + + loadMission(%mission, %missionType, false); + } +} + +// z0dd - ZOD, 5/23/03. New function, vote for CTF Anti-Turtle time setting +function serverCmdGetAntiTurtleTimeList( %client, %key ) +{ + if ( isObject( Game ) ) + Game.sendAntiTurtleTimeList( %client, %key ); +} + +//------------------------------------------------------------------ +// z0dd - ZOD- Founder, 7/13/03. From Mechina Mod, Admin punishments etc. + +function serverCmdTogglePlayerGag(%client, %who) +{ + if(%client.isSuperAdmin) + { + if(!%who.isGagged && !%who.isSuperAdmin) + { + %who.isGagged = true; + messageClient(%client, 'MsgAdmin', 'You have Gagged %1.', %who.name); + messageAllExcept(%who, -1, 'MsgAdminForce', '%1 has been Gagged by %2 for talking too much crap.', %who.name, %client.name); + messageClient(%who, 'MsgAdminAction', 'You have Been Gagged by %1, quit talking trash and play.', %client.name); + logEcho(%client.nameBase @ " gagged " @ %target.nameBase, 1); + } + else if (%who.isGagged) + { + %who.isGagged = false; + messageClient(%client, 'MsgAdmin', 'You have UnGagged %1.', %who.name); + messageAllExcept(%who, -1, 'MsgAdminAction', '%1 has been UnGagged by %2.', %who.name, %client.name); + messageClient(%who, 'MsgAdminAction', 'You have Been UnGagged by %1, quit talking trash and play.', %client.name); + logEcho(%client.nameBase @ " ungagged " @ %who.nameBase, 1); + } + } + else + messageClient(%client, 'MsgError', '\c2Only Super Admins can use this command.'); +} + +function serverCmdTogglePlayerFreeze(%client, %who) +{ + if(%client.isSuperAdmin) + { + if(!$MatchStarted) + { + messageClient(%client, 'MsgError', 'You must wait for the match to start!'); + return; + } + if (!%who.isFroze && !%who.isSuperAdmin) + { + if(!isobject(%who.player)) + { + messageClient(%client, 'MsgError', 'You must wait for the player to spawn!'); + return; + } + %who.isFroze = true; + %who.player.setvelocity("0 0 0"); + %who.player.setMoveState(true); + %who.player.invincible = true; + messageClient(%client, 'MsgAdmin', 'You have Frozen %1.', %who.name); + messageAllExcept(%who, -1, 'MsgAdminForce', '%1 has been Frozen by %2 for being a Llama.', %who.name, %client.name); + messageClient(%who, 'MsgAdminAction', 'You have Been Frozen by %1, Think about what you have been doing.', %client.name); + logEcho(%client.nameBase @ " froze " @ %who.nameBase, 1); + } + else if (%who.isFroze) + { + %who.isFroze = false; + %who.player.setMoveState(false); + %who.player.invincible = false; + messageClient(%client, 'MsgAdmin', 'You have de-iced %1.', %who.name); + messageAllExcept(%who, -1, 'MsgAdminForce', '%1 has been Un Frozen by %2.', %who.name, %client.name); + messageClient(%who, 'MsgAdminAction', 'You have Been de-Iced by %1, now behave.', %client.name); + logEcho(%client.nameBase @ " unfroze " @ %who.nameBase, 1); + } + } + else + messageClient(%client, 'MsgError', '\c2Only Super Admins can use this command.'); +} + +function serverCmdBootToTheRear(%client, %who) +{ + if(%client.isSuperAdmin) + { + if(!$MatchStarted) + { + messageClient(%client, 'MsgError', 'You must wait for the match to start!'); + return; + } + if(isObject(%who.player) && !%who.isSuperAdmin) + { + %time = getTime(); + %obj = %who.player; + %vec = "0 0 10"; + %obj.applyImpulse(%obj.position, VectorScale(%vec, %obj.getDataBlock().mass*20)); + messageAllExcept(%who, -1, 'MsgAdminForce', '%1 has been given a boot to the rear by %2.', %who.name, %client.name); + messageClient(%who, 'MsgAdminAction', 'You have Been given a boot to the ass by %1, now behave.', %client.name); + logEcho(%client.nameBase @ " gave " @ %who.nameBase @ " a boot to the rear", 1); + } + else + { + messageClient(%client, 'MsgError', 'You must wait for the player to spawn!'); + } + } + else + messageClient(%client, 'MsgError', '\c2Only Super Admins can use this command.'); +} + +function serverCmdExplodePlayer(%client, %who) +{ + if(%client.isSuperAdmin) + { + if(!$MatchStarted) + { + messageClient(%client, 'MsgError', 'You must wait for the match to start!'); + return; + } + if(isObject(%who.player) && !%who.isSuperAdmin) + { + %who.player.blowup(); + %who.player.scriptKill(0); + messageAllExcept(%who, -1, 'MsgAdminForce', '%1 found some explosives in his pants planted by %2.', %who.name, %client.name); + messageClient(%who, 'MsgAdminAction', 'You have Been dissasembled for inspection by the Super Admin %1, now behave.', %client.name); + logEcho(%client.nameBase @ " exploded " @ %who.nameBase, 1); + } + else + { + messageClient(%client, 'MsgError', 'You must wait for the player to spawn!'); + } + } + else + messageClient(%client, 'MsgError', '\c2Only Super Admins can use this command.'); +} + +// z0dd - ZOD - MeBad, 7/13/03. Send client information. +function ServerCmdPrintClientInfo(%client, %targetClient) +{ + if (%client.isAdmin) + { + if ((!%targetClient.isSuperAdmin) && (%client.isSuperAdmin)) + { + %wonid = getField( %targetClient.getAuthInfo(), 3); + %ip = %targetClient.getAddress(); + } + else + { + %wonid = "PROTECTED"; + %ip = "PROTECTED"; + } + MessageClient(%client, '', '---------------------------------------------------------------'); + MessageClient(%client, 'ClientInfo', "\c3Client Info...\n" @ + "ClientName: \c2" @ %targetClient.nameBase @ "\n" @ + "\c3Wonid: \c2" @ %wonid @ "\n" @ + "\c3IP: \c2" @ %ip @ "\n\n" @ + "\c3TeamKills:\c2 " @ %targetClient.teamkills @ "\n" @ + "\c3BK (BaseKills): \c2" @ %targetClient.tkDestroys @ "\n" @ + "\c3Suicides:\c2 " @ %targetClient.suicides @ "\n"); + MessageClient(%client, '', '---------------------------------------------------------------'); + } + else + messageClient(%client, 'MsgError', '\c2Only Admins can use this command.'); +} diff --git a/autoexec/AntiCloak.cs b/autoexec/AntiCloak.cs new file mode 100644 index 0000000..1300202 --- /dev/null +++ b/autoexec/AntiCloak.cs @@ -0,0 +1,15 @@ +$AntiCloakPlayerCount = 6; // amount of players needed on server for CloakPack to be selectable + +//Activates when default Inventory for player is set so player cant select it thru hotkeys or select it thru the gui. +function ActivateAntiCloak () +{ + //echo("TotalTeamPlayerCount " @ $TotalTeamPlayerCount); + //echo("AntiCloakPlayerCount " @ $AntiCloakPlayerCount); + + if(!$Host::TournamentMode && $TotalTeamPlayerCount < $AntiCloakPlayerCount) +//If server is in Tourny mode or if the server population isnt higher than the AntiCloakPlayerCount the CloakPack is not selectable. + $InvBanList[CTF, "CloakingPack"] = true; + else +//If AntiCloakPlayerCount is lower than server population, CloakPack is enabled and Selectable. + $InvBanList[CTF, "CloakingPack"] = false; +} \ No newline at end of file diff --git a/autoexec/GetTeamCounts.cs b/autoexec/GetTeamCounts.cs new file mode 100644 index 0000000..75db595 --- /dev/null +++ b/autoexec/GetTeamCounts.cs @@ -0,0 +1,89 @@ +//This function is Called at: +//CreateServer(%mission, %missionType) in Server.cs + +package StartTeamCounts { + +//Start +function CreateServer(%mission, %missionType) +{ + //Call default function + parent::CreateServer(%mission, %missionType); + //Start + //Call for a GetTeamCount update + GetTeamCounts( %game, %client, %respawn ); + +} + +}; + +// Prevent package from being activated if it is already +if (!isActivePackage(StartTeamCounts)) + activatePackage(StartTeamCounts); + +function GetTeamCounts( %game, %client, %respawn ) +{ + if (!isActivePackage(StartTeamCounts)) { + deactivatePackage(StartTeamCounts); + } + + //echo ("Clientgroup " @ ClientGroup.getCount()); + //echo ("$PlayerCount[0] " @ $PlayerCount[0]); + //echo ("$PlayerCount[1] " @ $PlayerCount[1]); + //echo ("$PlayerCount[2] " @ $PlayerCount[2]); + //echo ("client.team " @ %client.team); + + //Team Count code by Keen + $PlayerCount[0] = 0; + $PlayerCount[1] = 0; + $PlayerCount[2] = 0; + + for(%i = 0; %i < ClientGroup.getCount(); %i++) + { + %client = ClientGroup.getObject(%i); + + //if(!%client.isAIControlled()) + $PlayerCount[%client.team]++; + } + + //Other variables + //Amount of players on teams + $TotalTeamPlayerCount = $PlayerCount[1] + $PlayerCount[2]; + //Amount of all players including observers + $AllPlayerCount = $PlayerCount[1] + $PlayerCount[2] + $PlayerCount[0]; + + //Call Team Balance Notify + //Make sure it's CTF Mode + if($CurrentMissionType $= "CTF" && $TotalTeamPlayerCount !$= 0 && $countdownStarted $= true && $MatchStarted $= true) { + TeamBalanceNotify::AtSpawn( %game, %client, %respawn ); + } + //Start + //Make sure it's CTF Mode + if($CurrentMissionType $= "CTF" && $countdownStarted $= true && $MatchStarted $= true) { + PlayerNotify::AtSpawn( %game, %client, %respawn ); + } + //AntiCloak Start + //if($CurrentMissionType $= "CTF") { + //ActivateAntiCloak (); + //} + + //Call itself again. Every 5 seconds. + schedule(5000, 0, "GetTeamCounts"); + +} + +//For instant Calls for an update +//function QuickTeamCounts( %game, %client, %respawn ) +//{ + //Team Count code by Keen + //$PlayerCount[0] = 0; + //$PlayerCount[1] = 0; + //$PlayerCount[2] = 0; + + //for(%i = 0; %i < ClientGroup.getCount(); %i++) + //{ + //%client = ClientGroup.getObject(%i); + + //if(!%client.isAIControlled()) + //$PlayerCount[%client.team]++; + //} +//} diff --git a/autoexec/NoBaseRapeNotify.cs b/autoexec/NoBaseRapeNotify.cs new file mode 100644 index 0000000..a61cf12 --- /dev/null +++ b/autoexec/NoBaseRapeNotify.cs @@ -0,0 +1,70 @@ +//Start and Reset Notify +package NoRapeNotify { + +//Start Notify +//function DefaultGame::spawnPlayer( %game, %client, %respawn ) { + //Call default function + //parent::spawnPlayer( %game, %client, %respawn ); + //Start + //Make sure it's CTF Mode + //if( $CurrentMissionType $= "CTF" ) { + //PlayerNotify::AtSpawn( %game, %client, %respawn ); + //} +//} + +//Reset Notify +function DefaultGame::gameOver( %game ) { + //Call default function + parent::gameOver( %game ); + //Reset NoBaseRape Notify + ResetNotify::MissionEnd( %game, %client ); +} + +}; + +// Prevent package from being activated if it is already +if (!isActivePackage(NoRapeNotify)) + activatePackage(NoRapeNotify); + + + +//This function is at DefaultGame::spawnPlayer( %game, %client, %respawn ) defaultGame.cs +//Notifys the user if NoBase rape is on or off. Has a Counter so it is only run once and doesnt spam the client. It is triggered at spawn. +function PlayerNotify::AtSpawn( %game, %client, %respawn ) +{ + //echo ("%client " @ %client); + //echo ("$TeamBalanceClient " @ $TeamBalanceClient); + + //Is NoBaseRape On or off + if( !$Host::TournamentMode && $Host::EvoNoBaseRapeEnabled && $Host::EvoNoBaseRapeClassicPlayerCount > $TotalTeamPlayerCount ) { + //If on, has the client gotten the notification already + if($NoBaseRapeNotifyCount !$= 0) { + messageAll('MsgNoBaseRapeNotify', 'No Base Rape is \c3Enabled.~wfx/misc/nexus_cap.wav'); + $NoBaseRapeNotifyCount = 0; + } + } + else + //NoBaseRape is off + //Has the client gotten the notification already + if($NoBaseRapeNotifyCount !$= 1) { + messageAll('MsgNoBaseRapeNotify', 'No Base Rape is \c3Disabled.~wfx/misc/diagnostic_on.wav'); + $NoBaseRapeNotifyCount = 1; + } +} + +//This function is at StaticShapeData::damageObject(%data, %targetObject, %sourceObject, %position, %amount, %damageType) +//In the evopackage.cs or evoClassic.vl2 +//Plays a sound when a player hits a protected asset +function PlayerNotifyEnabled::OnDamage( %game, %sourceObject ) +{ + messageClient(%sourceObject.client, 'MsgNoBaseRapeNotify', '~wfx/misc/diagnostic_beep.wav'); +} + +//This function is at DefaultGame::gameOver(%game) CTFGame.cs +//Resets the client NotifyCount when the mission ends +function ResetNotify::MissionEnd( %game, %client ) +{ + $NoBaseRapeNotifyCount = -1; +} + + diff --git a/autoexec/TeamBalanceNotify.cs b/autoexec/TeamBalanceNotify.cs new file mode 100644 index 0000000..f4a3e6a --- /dev/null +++ b/autoexec/TeamBalanceNotify.cs @@ -0,0 +1,87 @@ +//Give the client a notification on the current state of balancing. +//This function is in GetTeamCounts( %game, %client, %respawn ) GetTeamCounts.cs +function TeamBalanceNotify::AtSpawn( %game, %client, %respawn ) +{ + //Call for a GetTeamCount update + //GetTeamCounts( %game, %client, %respawn ); + + //evoplayercount does not count yourself + + //variables + //%balancedifference = 2; //player difference you want to allow before sending notifications between teams. + //%Team1Difference = $PlayerCount[1] - $PlayerCount[2]; + //%Team2Difference = $PlayerCount[2] - $PlayerCount[1]; + + + //echo ("%Team1Difference " @ %Team1Difference); + //echo ("%Team2Difference " @ %Team2Difference); + + + //Are teams unbalanced? + if( $PlayerCount[1] !$= $PlayerCount[2] ) { + //Reset Balanced + $BalancedCount = 0; + if( ($PlayerCount[1] - $PlayerCount[2]) >= 2 || ($PlayerCount[2] - $PlayerCount[1]) >= 2 ) { + //Has the client gotten the notification already + if($TeamBalanceNotifyCount !$= 1) { + //If unbalanced, send a notification. Will continue to send notifications until teams are balanced. + messageAll('MsgTeamBalanceNotify', 'Teams are unbalanced.'); + //Only get the notification once per spawn. + $TeamBalanceNotifyCount = 1; + //Reset Stat + $StatsBalanceCount = 0; + } + } + } + else + //If teams are balanced and teams dont equal 0. + if( $PlayerCount[1] == $PlayerCount[2] && $TotalTeamPlayerCount !$= 0 ) { + //Has the client gotten the notification already + if($BalancedCount !$= 1) { + //If balanced, send a notification. + messageAll('MsgTeamBalanceNotify', 'Teams are balanced.'); + //Only get the balance notification once. + $BalancedCount = 1; + //Reset Unbalanced + $TeamBalanceNotifyCount = 0; + //Reset Stat + $StatsBalanceCount = 0; + } + } + + //3 or more difference gets a count notify + if( ($PlayerCount[1] - $PlayerCount[2]) >= 3 || ($PlayerCount[2] - $PlayerCount[1]) >= 3 ) { + //Run it once + if($StatsBalanceCount !$= 1) { + messageAll('MsgTeamBalanceNotify', 'It is currently %1 vs %2 with %3 observers.', $PlayerCount[1], $PlayerCount[2], $PlayerCount[0] ); + $StatsBalanceCount = 1; + } + } +} + +//Called in evo in CTFGame.ovl +function ResetUnbalancedNotifyPerCap() +{ + $TeamBalanceNotifyCount = 0; + $StatsBalanceCount = 0; +} + +//Start and Reset Notify +package TeamCountNotify { + +//Reset Notify +function DefaultGame::gameOver( %game ) { + //Call default function + parent::gameOver( %game ); + //Reset TeamBalance Variables + $BalancedCount = -1; + $TeamBalanceNotifyCount = -1; + $StatsBalanceCount = -1; + +} + +}; + +// Prevent package from being activated if it is already +if (!isActivePackage(TeamCountNotify)) + activatePackage(TeamCountNotify); \ No newline at end of file diff --git a/autoexec/antiTurret.cs b/autoexec/antiTurret.cs new file mode 100644 index 0000000..c7239d6 --- /dev/null +++ b/autoexec/antiTurret.cs @@ -0,0 +1,19 @@ +$TurretPlayerCount = 10; // amount of players needed on server for turrets + +package antiTurret { + +function TurretData::selectTarget(%this, %turret) +{ + if( !$Host::TournamentMode && $TotalTeamPlayerCount < $TurretPlayerCount) { + %turret.clearTarget(); + } + else { + Parent::selectTarget(%this, %turret); + } +} + +}; + +// Prevent package from being activated if it is already +if (!isActivePackage(antiTurret)) + activatePackage(antiTurret); \ No newline at end of file diff --git a/autoexec/anti_NoFog_Snipe.cs b/autoexec/anti_NoFog_Snipe.cs new file mode 100644 index 0000000..a714b99 --- /dev/null +++ b/autoexec/anti_NoFog_Snipe.cs @@ -0,0 +1,21 @@ +// anti NoFog Snipe by Red Shifter +// A far cry to the solution of noFog, but this'll stop the snipes +// This is a Server-Side Script + +package antiNoFogSnipe { + +function DefaultGame::missionLoadDone(%game) { + +Parent::missionLoadDone(%game); + +if (Sky.visibleDistance $= "" || Sky.visibleDistance == 0) { +// This script plays it safe. You better have a map that works. +error("WARNING! This map will not work with NoFog Snipe!"); +BasicSniperShot.maxRifleRange = 1000; +} +else +BasicSniperShot.maxRifleRange = Sky.visibleDistance; +} + +}; +activatePackage(antiNoFogSnipe); diff --git a/autoexec/checkver.cs b/autoexec/checkver.cs new file mode 100644 index 0000000..d7873f6 --- /dev/null +++ b/autoexec/checkver.cs @@ -0,0 +1,77 @@ +// TribesNext Minimum Version Enforcement +// Written by Thyth +// 2014-08-18 + +// Updated on 2014-08-31 after testing/feedback from Heat Killer. + +// This script prevents clients from joining a non-observer team if they are not running +// TribesNext RC2a or newer, with the tournamentNetClient.vl2 installed. An early form of +// anticheat was added to the RC2 patch that kills HM2. This script allows detecting of +// a new enough version by the interaction with the TribesNext community/browser system. +// Support for clan tags (and account renaming) was added along with the HM2 killer in RC2, +// but no client side code to talk to the browser server was in yet. Now that the browser +// system backend is complete, all clients can install the tournamentNetClient to the +// browser, and users running RC2 (with HM2 killer) can be detected. + +// The variable on the client object: +// %client.t2csri_sentComCertDone +// Will be 1 if they are running RC2+ with tournamentNetClient.vl2 + +// Admins can override this restriction when forcing players to join a team. + +function checkVer_showBanner(%client) +{ + // customize me + commandToClient(%client, 'CenterPrint', "Version Check Failed!\nYou need the latest TribesNext patch and TournyNetClient to play.\n Download it from T2Discord.tk and drop it into your GameData/Base folder.", 10, 3); +} + +package checkver +{ + function serverCmdClientJoinTeam(%client, %team) + { + if (!%client.t2csri_sentComCertDone) + { + checkVer_showBanner(%client); + return; + } + Parent::serverCmdClientJoinTeam(%client, %team); + } + function serverCmdClientJoinGame(%client) + { + if (!%client.t2csri_sentComCertDone) + { + checkVer_showBanner(%client); + return; + } + Parent::serverCmdClientJoinGame(%client); + } + function serverCmdClientPickedTeam(%client, %option) + { + if (!%client.t2csri_sentComCertDone) + { + checkVer_showBanner(%client); + return; + } + Parent::serverCmdClientPickedTeam(%client, %option); + } + function serverCmdClientTeamChange(%client, %option) + { + if (!%client.t2csri_sentComCertDone) + { + checkVer_showBanner(%client); + return; + } + Parent::serverCmdClientTeamChange(%client, %option); + } + function Observer::onTrigger(%data, %obj, %trigger, %state) + { + %client = %obj.getControllingClient(); + if (!%client.t2csri_sentComCertDone) + { + checkVer_showBanner(%client); + return; + } + Parent::onTrigger(%data, %obj, %trigger, %state); + } +}; +activatePackage(checkver); diff --git a/autoexec/noMortarTurret.cs b/autoexec/noMortarTurret.cs new file mode 100644 index 0000000..22a8257 --- /dev/null +++ b/autoexec/noMortarTurret.cs @@ -0,0 +1,19 @@ +// ban mortar turret from inventory in main gametypes +$InvBanList[CTF, "MortarBarrelPack"] = 1; +$InvBanList[CnH, "MortarBarrelPack"] = 1; +$InvBanList[Siege, "MortarBarrelPack"] = 1; + +package noMortarTurret { + +// if a mortar turret somehow makes it into the game, keep it from working +function TurretData::selectTarget(%this, %turret) { + if( %turret.initialBarrel !$= "MortarBarrelLarge" ) { + Parent::selectTarget(%this, %turret); + } +} + +}; + +// Prevent package from being activated if it is already +if (!isActivePackage( noMortarTurret )) + activatePackage( noMortarTurret ); diff --git a/defaultGame.cs b/defaultGame.cs new file mode 100644 index 0000000..8d2275b --- /dev/null +++ b/defaultGame.cs @@ -0,0 +1,3939 @@ +//$MissionName is the file name of the mission +//$MapName is the displayed name(no underscore,spaces) +//$GameType (CTF,Hunters) + + +function DefaultGame::activatePackages(%game) +{ + // activate the default package for the game type + activatePackage(DefaultGame); + if(isPackage(%game.class) && %game.class !$= DefaultGame) + activatePackage(%game.class); +} + +function DefaultGame::deactivatePackages(%game) +{ + // z0dd - ZOD, 5/19/03. Check to see if its active first + if(isActivePackage(DefaultGame)) + deactivatePackage(DefaultGame); + + if(isPackage(%game.class) && %game.class !$= DefaultGame) + { + if(isActivePackage(%game.class)) + deactivatePackage(%game.class); + } +} + +package DefaultGame { + +function FlipFlop::objectiveInit(%data, %flipflop) +{ + // add this flipflop to missioncleanup + %flipflopSet = nameToID("MissionCleanup/FlipFlops"); + if(%flipflopSet <= 0) { + %flipflopSet = new SimSet("FlipFlops"); + MissionCleanup.add(%flipflopSet); + } + %flipflopSet.add(%flipflop); + + // see if there's a holo projector associated with this flipflop + // search the flipflop's folder for a holo projector + // if one exists, associate it with the flipflop + + %flipflop.projector = 0; + %folder = %flipflop.getGroup(); + for(%i = 0; %i < %folder.getCount(); %i++) + { + %proj = %folder.getObject(%i); + // weird, but line below prevents console error + //if(%proj.getClassName() !$= "SimGroup" && %proj.getClassName() !$= "InteriorInstance") + // z0dd - ZOD, 5/19/03. Added TSStatic for spam fix + if(%proj.getClassName() !$= "SimGroup" && %proj.getClassName() !$= "InteriorInstance" && %proj.getClassName() !$= "TSStatic") + { + if(%proj.getDatablock().getName() $= "LogoProjector") + { + %flipflop.projector = %proj; + %flipflop.projector.holo = 0; + break; + } + } + } + + // may have been hidden + %target = %flipFlop.getTarget(); + if(%target != -1) + { + // set flipflop to base skin + setTargetSkin(%target, $teamSkin[0]); + + // make this always visible in the commander map + setTargetAlwaysVisMask(%target, 0xffffffff); + + // make this always visible in the commander list + setTargetRenderMask(%target, getTargetRenderMask(%target) | $TargetInfo::CommanderListRender); + } +} + +function FlipFlop::playerTouch(%data, %flipflop, %player) +{ + %client = %player.client; + %flipTeam = %flipflop.team; + + if(%flipTeam == %client.team) + return false; + + %teamName = game.getTeamName(%client.team); + // Let the observers know: + messageTeam( 0, 'MsgClaimFlipFlop', '\c2%1 claimed %2 for %3.~wfx/misc/flipflop_taken.wav', %client.name, Game.cleanWord( %flipflop.name ), %teamName ); + // Let the teammates know: + messageTeam( %client.team, 'MsgClaimFlipFlop', '\c2%1 claimed %2 for %3.~wfx/misc/flipflop_taken.wav', %client.name, Game.cleanWord( %flipflop.name ), %teamName ); + // Let the other team know: + %losers = %client.team == 1 ? 2 : 1; + messageTeam( %losers, 'MsgClaimFlipFlop', '\c2%1 claimed %2 for %3.~wfx/packs/shield_hit.wav', %client.name, Game.cleanWord( %flipflop.name ), %teamName ); // z0dd - ZOD, 10/30/02. Change flipflop lost sound + + logEcho(%client.nameBase@" (pl "@%player@"/cl "@%client@") claimed flipflop "@%flipflop@" for team "@%client.team); + + //change the skin on the switch to claiming team's logo + setTargetSkin(%flipflop.getTarget(), game.getTeamSkin(%player.team)); + setTargetSensorGroup(%flipflop.getTarget(), %player.team); + + // if there is a "projector" associated with this flipflop, put the claiming team's logo there + if(%flipflop.projector > 0) + { + %projector = %flipflop.projector; + // axe the old projected holo, if one exists + if(%projector.holo > 0) + %projector.holo.delete(); + + %newHolo = getTaggedString(game.getTeamSkin(%client.team)) @ "Logo"; + + %projTransform = %projector.getTransform(); + // below two functions are from deployables.cs + %projRot = rotFromTransform(%projTransform); + %projPos = posFromTransform(%projTransform); + // place the holo above the projector (default 10 meters) + %hHeight = %projector.holoHeight; + if(%hHeight $= "") + %hHeight = 10; + %holoZ = getWord(%projPos, 2) + %hHeight; + %holoPos = firstWord(%projPos) SPC getWord(%projPos,1) SPC %holoZ; + + %holo = new StaticShape() + { + rotation = %projRot; + position = %holoPos; + dataBlock = %newHolo; + }; + // dump the hologram into MissionCleanup + MissionCleanup.add(%holo); + // associate the holo with the projector + %projector.holo = %holo; + } + + // convert the resources associated with the flipflop + Game.claimFlipflopResources(%flipflop, %client.team); + + if(Game.countFlips()) + for(%i = 1; %i <= Game.numTeams; %i++) + { + %teamHeld = Game.countFlipsHeld(%i); + messageAll('MsgFlipFlopsHeld', "", %i, %teamHeld); + } + + //call the ai function + if(%player.client.isAIControlled()) // z0dd - ZOD, 5/19/03. check to see if the player is a bot, duh. + Game.AIplayerCaptureFlipFlop(%player, %flipflop); + + return true; +} +}; + +//--------- DEFAULT SCORING, SUPERCEDE IN GAMETYPE FILE ------------------ + +function DefaultGame::initGameVars(%game) +{ + %game.SCORE_PER_SUICIDE = 0; + %game.SCORE_PER_TEAMKILL = 0; + %game.SCORE_PER_DEATH = 0; + + %game.SCORE_PER_KILL = 0; + + %game.SCORE_PER_TURRET_KILL = 0; + + $EndCountdownStart = false; +} + +//-- tracking --- +// .deaths .kills .suicides .teamKills .turretKills + +function DefaultGame::claimFlipflopResources(%game, %flipflop, %team) +{ + %group = %flipflop.getGroup(); + %group.setTeam(%team); + + // make this always visible in the commander map (gets reset when sensor group gets changed) + setTargetAlwaysVisMask(%flipflop.getTarget(), 0xffffffff); +} + +//------------------------------------------------------------------------------ +function DefaultGame::selectSpawnSphere(%game, %team) +{ + // - walks the objects in the 'teamdrops' group for this team + // - find a random spawn point which has a running sum less more than + // 0->total sphere weight + + %teamDropsGroup = "MissionCleanup/TeamDrops" @ %team; + + %group = nameToID(%teamDropsGroup); + if (%group != -1) + { + %count = %group.getCount(); + if (%count != 0) + { + // Get total weight of those spheres not filtered by mission types list- + %overallWeight = 0; + for (%i = 0; %i < %count; %i++) + { + %sphereObj = %group.getObject(%i); + if ( ! %sphereObj.isHidden() ) + %overallWeight += %sphereObj.sphereWeight; + } + + if (%overallWeight > 0) + { + // Subtract a little from this as hedge against any rounding offness- + %randSum = getRandom(%overallWeight) - 0.05; + // echo("randSum = " @ %randSum); + + for (%i = 0; %i < %count; %i++) + { + %sphereObj = %group.getObject(%i); + if (! %sphereObj.isHidden()) + { + %randSum -= %sphereObj.sphereWeight; + if (%randSum <= 0) + { + // echo("Chose sphere " @ %i); + return %group.getObject(%i); // Found our sphere + } + } + } + error("Random spawn sphere selection didn't work"); + } + else + error("No non-hidden spawnspheres were found in " @ %teamDropsGroup); + } + else + error("No spawnspheres found in " @ %teamDropsGroup); + } + else + error(%teamDropsGroup @ " not found in selectSpawnSphere()."); + + return -1; +} + +function DefaultGame::selectSpawnZone(%game, %sphere) +{ + // determines if this should spawn inside or outside + %overallWeight = %sphere.indoorWeight + %sphere.outdoorWeight; + %index = mFloor(getRandom() * (%overallWeight - 0.1)) + 1; + if ((%index - %sphere.indoorWeight) > 0) + return false; //do not pick an indoor spawn + else + return true; //pick an indoor spawn +} + +function DefaultGame::selectSpawnFacing(%game, %src, %target, %zone) +{ + //this used only when spawn loc is not on an interior. This points spawning player to the ctr of spawnshpere + %target = setWord(%target, 2, 0); + %src = setWord(%src, 2, 0); + + if(VectorDist(%target, %src) == 0) + return " 0 0 1 0 "; + %vec = VectorNormalize(VectorSub(%target, %src)); + %angle = mAcos(getWord(%vec, 1)); + + if(%src < %target) + return(" 0 0 1 " @ %angle); + else + return(" 0 0 1 " @ -%angle); +} + +function DefaultGame::pickTeamSpawn(%game, %team) +{ + // early exit if no nav graph + if (!navGraphExists()) + { + echo("No navigation graph is present. Build one."); + return -1; + } + + for (%attempt = 0; %attempt < 20; %attempt++) + { + // finds a random spawn sphere + // selects inside/outside on this random sphere + // if the navgraph exists, then uses it to grab a random node as spawn + // location/rotation + %sphere = %game.selectSpawnSphere(%team); + if (%sphere == -1) + { + echo("No spawn spheres found for team " @ %team); + return -1; + } + + %zone = %game.selectSpawnZone(%sphere); + %useIndoor = %zone; + %useOutdoor = !%zone; + if (%zone) + %area = "indoor"; + else + %area = "outdoor"; + + %radius = %sphere.radius; + %sphereTrans = %sphere.getTransform(); + %sphereCtr = getWord(%sphereTrans, 0) @ " " @ getWord(%sphereTrans, 1) @ " " @ getWord(%sphereTrans, 2); //don't need full transform here, just x, y, z + //echo("Selected Sphere is " @ %sphereCtr @ " with a radius of " @ %radius @ " meters. Selecting from " @ %area @ " zone."); + + %avoidThese = $TypeMasks::VehicleObjectType | $TypeMasks::MoveableObjectType | + $TypeMasks::PlayerObjectType | $TypeMasks::TurretObjectType; + + for (%tries = 0; %tries < 10; %tries++) + { + %nodeIndex = navGraph.randNode(%sphereCtr, %radius, %useIndoor, %useOutdoor); + if (%nodeIndex >= 0) + { + %loc = navGraph.randNodeLoc(%nodeIndex); + %adjUp = VectorAdd(%loc, "0 0 1.0"); // don't go much below + + if (ContainerBoxEmpty( %avoidThese, %adjUp, 2.0)) + break; + } + } + + if (%nodeIndex >= 0) + { + %loc = navGraph.randNodeLoc(%nodeIndex); + if (%zone) + { + %trns = %loc @ " 0 0 1 0"; + %spawnLoc = whereToLook(%trns); + } + else + { + %rot = %game.selectSpawnFacing(%loc, %sphereCtr, %zone); + %spawnLoc = %loc @ %rot; + } + return %spawnLoc; + } + } +} + +//------------------------------------------------------------ + +function DefaultGame::pickObserverSpawn(%game, %client, %next) +{ + %group = nameToID("MissionGroup/ObserverDropPoints"); + %count = %group.getCount(); + + if(!%count || %group == -1) + { + echo("no observer spawn points found"); + return -1; + } + + if(%client.lastObserverSpawn == -1) + { + %client.lastObserverSpawn = 0; + return(%group.getObject(%client.lastObserverSpawn)); + } + + if(%next == true) + %spawnIdx = %client.lastObserverSpawn + 1; + else + %spawnIdx = %client.lastObserverSpawn - 1; + + if(%spawnIdx < 0) + %spawnIdx = %count - 1; + else if(%spawnIdx >= %count) + %spawnIdx = 0; + + %client.lastObserverSpawn = %spawnIdx; + //echo("Observer spawn point found"); + return %group.getObject(%spawnIdx); +} + +//------------------------------------------------------------ +function DefaultGame::spawnPlayer( %game, %client, %respawn ) +{ + %client.lastSpawnPoint = %game.pickPlayerSpawn( %client, false ); + %client.suicidePickRespawnTime = getSimTime() + 20000; + %game.createPlayer( %client, %client.lastSpawnPoint, %respawn ); + +} + + +//------------------------------------------------------------ +function DefaultGame::playerSpawned(%game, %player) +{ + if( %player.client.respawnTimer ) + cancel(%player.client.respawnTimer); + + %player.client.observerStartTime = ""; + %game.equip(%player); + + //set the spawn time (for use by the AI system) + %player.client.spawnTime = getSimTime(); + +// jff: this should probably be checking the team of the client + //update anyone observing this client + %count = ClientGroup.getCount(); + for (%i = 0; %i < %count; %i++) + { + %cl = ClientGroup.getObject(%i); + if (%cl.camera.mode $= "observerFollow" && %cl.observeClient == %player.client) + { + %transform = %player.getTransform(); + %cl.camera.setOrbitMode(%player, %transform, 0.5, 4.5, 4.5); + %cl.camera.targetObj = %player; + } + } +} + +function DefaultGame::equip(%game, %player) +{ + for(%i =0; %i<$InventoryHudCount; %i++) + %player.client.setInventoryHudItem($InventoryHudData[%i, itemDataName], 0, 1); + %player.client.clearBackpackIcon(); + + //%player.setArmor("Light"); + %player.setInventory(RepairKit,1); + %player.setInventory(Grenade,6); + %player.setInventory(Blaster,1); + %player.setInventory(Disc,1); + %player.setInventory(Chaingun, 1); + %player.setInventory(ChaingunAmmo, 100); + %player.setInventory(DiscAmmo, 20); + %player.setInventory(Beacon, 3); + %player.setInventory(TargetingLaser, 1); + %player.weaponCount = 3; + + %player.use("Blaster"); +} + +//------------------------------------------------------------ +function DefaultGame::pickPlayerSpawn(%game, %client, %respawn) +{ + // place this client on his own team, '%respawn' does not ever seem to be used + //we no longer care whether it is a respawn since all spawns use same points. + return %game.pickTeamSpawn(%client.team); +} + +//------------------------------------------------------------ +function DefaultGame::createPlayer(%game, %client, %spawnLoc, %respawn) +{ + // do not allow a new player if there is one (not destroyed) on this client + if(isObject(%client.player) && (%client.player.getState() !$= "Dead")) + return; + + // clients and cameras can exist in team 0, but players should not + if(%client.team == 0) + error("Players should not be added to team0!"); + + // defaultplayerarmor is in 'players.cs' + if(%spawnLoc == -1) + %spawnLoc = "0 0 300 1 0 0 0"; + //else + // echo("Spawning player at " @ %spawnLoc); + + // copied from player.cs + if (%client.race $= "Bioderm") + // Only have male bioderms. + %armor = $DefaultPlayerArmor @ "Male" @ %client.race @ Armor; + else + %armor = $DefaultPlayerArmor @ %client.sex @ %client.race @ Armor; + %client.armor = $DefaultPlayerArmor; + + %player = new Player() { + //dataBlock = $DefaultPlayerArmor; + dataBlock = %armor; + }; + + + if(%respawn) + { + %player.setInvincible(true); + //%player.setCloaked(true); // z0dd - ZOD, 8/6/02. Don't spawn players cloaked + %player.setInvincibleMode($InvincibleTime,0.02); + //%player.respawnCloakThread = %player.schedule($InvincibleTime * 1000, "setRespawnCloakOff"); // z0dd - ZOD, 8/6/02. Don't spawn players cloaked + %player.schedule($InvincibleTime * 1000, "setInvincible", false); + } + + %player.setTransform( %spawnLoc ); + MissionCleanup.add(%player); + + // setup some info + %player.setOwnerClient(%client); + %player.team = %client.team; + %client.outOfBounds = false; + %player.setEnergyLevel(60); + %client.player = %player; + // z0dd - ZOD, 5/07/04. Bots need some help with this gravity + if(%client.isAiControlled()) + %player.setRechargeRate(%player.getDataBlock().rechargeRate + 0.20); + + // updates client's target info for this player + %player.setTarget(%client.target); + setTargetDataBlock(%client.target, %player.getDatablock()); + setTargetSensorData(%client.target, PlayerSensor); + setTargetSensorGroup(%client.target, %client.team); + %client.setSensorGroup(%client.team); + + //make sure the player has been added to the team rank array... + %game.populateTeamRankArray(%client); + + %game.playerSpawned(%client.player); +} + +function Player::setRespawnCloakOff(%player) +{ + %player.setCloaked(false); + %player.respawnCloakThread = ""; +} + +//------------------------------------------------------------ + +function DefaultGame::startMatch(%game) +{ + echo("START MATCH"); + MessageAll('MsgMissionStart', "\c2Match started!"); + + //the match has been started, clear the team rank array, and repopulate it... + for (%i = 0; %i < 32; %i++) + %game.clearTeamRankArray(%i); + + //used in BountyGame, prolly in a few others as well... + $matchStarted = true; + + %game.clearDeployableMaxes(); + + $missionStartTime = getSimTime(); + %curTimeLeftMS = ($Host::TimeLimit * 60 * 1000); + + // schedule first timeLimit check for 20 seconds + if(%game.class !$= "SiegeGame") + { + %game.timeCheck = %game.schedule(20000, "checkTimeLimit"); + } + + //schedule the end of match countdown + EndCountdown($Host::TimeLimit * 60 * 1000); + + //reset everyone's score and add them to the team rank array + for (%i = 0; %i < ClientGroup.getCount(); %i++) + { + %cl = ClientGroup.getObject(%i); + %game.resetScore(%cl); + %game.populateTeamRankArray(%cl); + } + + // set all clients control to their player + %count = ClientGroup.getCount(); + for( %i = 0; %i < %count; %i++ ) + { + %cl = ClientGroup.getObject(%i); + + // Siege game will set the clock differently + if(%game.class !$= "SiegeGame") + messageClient(%cl, 'MsgSystemClock', "", $Host::TimeLimit, %curTimeLeftMS); + + if( !$Host::TournamentMode && %cl.matchStartReady && %cl.camera.mode $= "pre-game") + { + commandToClient(%cl, 'setHudMode', 'Standard'); + %cl.setControlObject( %cl.player ); + } + else + { + if( %cl.matchStartReady ) + { + if(%cl.camera.mode $= "pre-game") + { + %cl.observerMode = ""; + commandToClient(%cl, 'setHudMode', 'Standard'); + + if(isObject(%cl.player)) + %cl.setControlObject( %cl.player ); + else + echo("can't set control for client: " @ %cl @ ", no player object found!"); + } + else + %cl.observerMode = "observerFly"; + } + } + } + + // on with the show this is it! + AISystemEnabled( true ); +} + +function DefaultGame::gameOver( %game ) +{ + //set the bool + $missionRunning = false; + + CancelCountdown(); + CancelEndCountdown(); + + //loop through all the clients, and do any cleanup... + %count = ClientGroup.getCount(); + for (%i = 0; %i < %count; %i++) + { + %client = ClientGroup.getObject(%i); + %player = %client.player; + + // z0dd - ZOD, 6/13/02. Need to remove this for random teams by Founder (founder@mechina.com). + if($CurrentMissionType $= TR2) // z0dd - ZOD, 9/17/02. Check for Team Rabbit 2 + %client.lastTeam = %client.team; + + if ( !%client.isAiControlled() ) + { + %client.endMission(); + messageClient( %client, 'MsgClearDebrief', "" ); + %game.sendDebriefing( %client ); + + if(%client.player.isBomber) + commandToClient(%client, 'endBomberSight'); + + //clear the score hud... + messageClient( %client, 'SetScoreHudHeader', "", "" ); + messageClient( %client, 'SetScoreHudSubheader', "", ""); + messageClient( %client, 'ClearHud', "", 'scoreScreen', 0 ); + + // clean up the players' HUDs: + %client.setWeaponsHudClearAll(); + %client.setInventoryHudClearAll(); + } + } + // z0dd - ZOD, 6/22/02. Setup random teams by Founder (founder@mechina.com). + if($CurrentMissionType !$= TR2) // z0dd - ZOD, 9/17/02. Check for Team Rabbit 2 + %game.setupClientTeams(); + + // Default game does nothing... except lets the AI know the mission is over + AIMissionEnd(); + + ResetNotify::MissionEnd( %game, %client ); + +} + +//------------------------------------------------------------------------------ +function DefaultGame::sendDebriefing( %game, %client ) +{ + if ( %game.numTeams == 1 ) + { + // Mission result: + %winner = $TeamRank[0, 0]; + if ( %winner.score > 0 ) + messageClient( %client, 'MsgDebriefResult', "", '%1 wins!', $TeamRank[0, 0].name ); + else + messageClient( %client, 'MsgDebriefResult', "", 'Nobody wins.' ); + + // Player scores: + %count = $TeamRank[0, count]; + messageClient( %client, 'MsgDebriefAddLine', "", 'PLAYERSCOREKILLS' ); + for ( %i = 0; %i < %count; %i++ ) + { + %cl = $TeamRank[0, %i]; + if ( %cl.score $= "" ) + %score = 0; + else + %score = %cl.score; + if ( %cl.kills $= "" ) + %kills = 0; + else + %kills = %cl.kills; + messageClient( %client, 'MsgDebriefAddLine', "", ' %1 %2 %3', %cl.name, %score, %kills ); + } + } + else + { + %topScore = ""; + %topCount = 0; + for ( %team = 1; %team <= %game.numTeams; %team++ ) + { + if ( %topScore $= "" || $TeamScore[%team] > %topScore ) + { + %topScore = $TeamScore[%team]; + %firstTeam = %team; + %topCount = 1; + } + else if ( $TeamScore[%team] == %topScore ) + { + %secondTeam = %team; + %topCount++; + } + } + + // Mission result: + if ( %topCount == 1 ) + messageClient( %client, 'MsgDebriefResult', "", 'Team %1 wins!', %game.getTeamName(%firstTeam) ); + else if ( %topCount == 2 ) + messageClient( %client, 'MsgDebriefResult', "", 'Team %1 and Team %2 tie!', %game.getTeamName(%firstTeam), %game.getTeamName(%secondTeam) ); + else + messageClient( %client, 'MsgDebriefResult', "", 'The mission ended in a tie.' ); + + // Team scores: + messageClient( %client, 'MsgDebriefAddLine', "", 'TEAMSCORE' ); + for ( %team = 1; %team - 1 < %game.numTeams; %team++ ) + { + if ( $TeamScore[%team] $= "" ) + %score = 0; + else + %score = $TeamScore[%team]; + messageClient( %client, 'MsgDebriefAddLine', "", ' %1 %2', %game.getTeamName(%team), %score ); + } + + // Player scores: + messageClient( %client, 'MsgDebriefAddLine', "", '\nPLAYERTEAMSCOREKILLS' ); + for ( %team = 1; %team - 1 < %game.numTeams; %team++ ) + %count[%team] = 0; + + %notDone = true; + while ( %notDone ) + { + // Get the highest remaining score: + %highScore = ""; + for ( %team = 1; %team <= %game.numTeams; %team++ ) + { + if ( %count[%team] < $TeamRank[%team, count] && ( %highScore $= "" || $TeamRank[%team, %count[%team]].score > %highScore ) ) + { + %highScore = $TeamRank[%team, %count[%team]].score; + %highTeam = %team; + } + } + + // Send the debrief line: + %cl = $TeamRank[%highTeam, %count[%highTeam]]; + %score = %cl.score $= "" ? 0 : %cl.score; + %kills = %cl.kills $= "" ? 0 : %cl.kills; + messageClient( %client, 'MsgDebriefAddLine', "", ' %1 %2 %3 %4', %cl.name, %game.getTeamName(%cl.team), %score, %kills ); + + %count[%highTeam]++; + %notDone = false; + for ( %team = 1; %team - 1 < %game.numTeams; %team++ ) + { + if ( %count[%team] < $TeamRank[%team, count] ) + { + %notDone = true; + break; + } + } + } + } + + //now go through an list all the observers: + %count = ClientGroup.getCount(); + %printedHeader = false; + for (%i = 0; %i < %count; %i++) + { + %cl = ClientGroup.getObject(%i); + if (%cl.team <= 0) + { + //print the header only if we actually find an observer + if (!%printedHeader) + { + %printedHeader = true; + messageClient(%client, 'MsgDebriefAddLine', "", '\nOBSERVERSSCORE'); + } + + //print out the client + %score = %cl.score $= "" ? 0 : %cl.score; + messageClient( %client, 'MsgDebriefAddLine', "", ' %1 %2', %cl.name, %score); + } + } +} + +//------------------------------------------------------------ +function DefaultGame::clearDeployableMaxes(%game) +{ + for(%i = 0; %i <= %game.numTeams; %i++) + { + $TeamDeployedCount[%i, TurretIndoorDeployable] = 0; + $TeamDeployedCount[%i, TurretOutdoorDeployable] = 0; + $TeamDeployedCount[%i, PulseSensorDeployable] = 0; + $TeamDeployedCount[%i, MotionSensorDeployable] = 0; + $TeamDeployedCount[%i, InventoryDeployable] = 0; + $TeamDeployedCount[%i, DeployedCamera] = 0; + $TeamDeployedCount[%i, MineDeployed] = 0; + $TeamDeployedCount[%i, TargetBeacon] = 0; + $TeamDeployedCount[%i, MarkerBeacon] = 0; + + // z0dd - ZOD, 5/27/03. Reset armor counts + $TeamArmorCount[%i, Light] = 0; + $TeamArmorCount[%i, Medium] = 0; + $TeamArmorCount[%i, Heavy] = 0; + } +} + +// called from player scripts +function DefaultGame::onClientDamaged(%game, %clVictim, %clAttacker, %damageType, %sourceObject) +{ + //set the vars if it was a turret + if (isObject(%sourceObject)) + { + %sourceClassType = %sourceObject.getDataBlock().getClassName(); + %sourceType = %sourceObject.getDataBlock().getName(); + } + + if (%sourceClassType $= "TurretData") + { + // jff: are there special turret types which makes this needed? + // tinman: yes, we don't want bots stopping to fire on the big outdoor turrets, which they + // will just get mowed down. deployables only. + if (%sourceType $= "TurretDeployedFloorIndoor" || %sourceType $= "TurretDeployedWallIndoor" || + %sourceType $= "TurretDeployedCeilingIndoor" || %sourceType $= "TurretDeployedOutdoor") + { + %clVictim.lastDamageTurretTime = getSimTime(); + %clVictim.lastDamageTurret = %sourceObject; + } + + %turretAttacker = %sourceObject.getControllingClient(); + + //------------------------------------------------------------------- + // z0dd - ZOD, 5/29/02. Play a sound to client when they hit a player + if(%turretAttacker) + { + %client = %turretAttacker; + } + //------------------------------------------------------------------- + + // should get a damagae message from friendly fire turrets also + if(%turretAttacker && %turretAttacker != %clVictim && %turretAttacker.team == %clVictim.team) + { + if (%game.numTeams > 1 && %turretAttacker.player.causedRecentDamage != %clVictim.player) //is a teamgame & player just damaged a teammate + { + %turretAttacker.player.causedRecentDamage = %clVictim.player; + %turretAttacker.player.schedule(1000, "causedRecentDamage", ""); //allow friendly fire message every x ms + %game.friendlyFireMessage(%clVictim, %turretAttacker); + } + } + } + else if (%sourceClassType $= "PlayerData") + { + %client = %clAttacker; // z0dd - ZOD, 5/29/02. Play a sound to client when they hit a player + //now see if both were on the same team + if(%clAttacker && %clAttacker != %clVictim && %clVictim.team == %clAttacker.team) + { + if (%game.numTeams > 1 && %clAttacker.player.causedRecentDamage != %clVictim.player) //is a teamgame & player just damaged a teammate + { + %clAttacker.player.causedRecentDamage = %clVictim.player; + %clAttacker.player.schedule(1000, "causedRecentDamage", ""); //allow friendly fire message every x ms + %game.friendlyFireMessage(%clVictim, %clAttacker); + } + } + if (%clAttacker && %clAttacker != %clVictim) + { + %clVictim.lastDamageTime = getSimTime(); + %clVictim.lastDamageClient = %clAttacker; + if (%clVictim.isAIControlled()) + %clVictim.clientDetected(%clAttacker); + } + } + // ------------------------------------------------------------------ + // z0dd - ZOD, 5/29/02. Play a sound to client when they hit a player + else if( %sourceClassType $= "WheeledVehicleData" || + %sourceClassType $= "FlyingVehicleData" || + %sourceClassType $= "HoverVehicleData" ) + { + if (%sourceObject.getControllingClient()) + { + %client = %sourceObject.getControllingClient(); + } + } + + if ( %client && %client.playerHitSound && ($CurrentMissionType !$= TR2)) + { + // 1) Blaster + // 2) Plasma Gun + // 3) Chaingun + // 4) Disc + // 5) Grenades (GL and hand) + // 6) Laser + // 8) Mortar + // 9) Missile + // 10) ShockLance + + // 13) Impact (object to object) + + // 16) Plasma Turret + // 17) AA Turret + // 18) ELF Turret + // 19) Mortar Turret + // 20) Missile Turret + // 21) Indoor Deployable Turret + // 22) Outdoor Deployable Turret + // 23) Sentry Turret + + // 26) Shrike Blaster + // 27) Bobmer Plasma + // 28) Bomber Bomb + // 29) Tank Chaingun + // 30) Tank Mortar + // 31) Satchel + if (%client.team != %clVictim.team) + { + if ((%damageType > 0 && %damageType < 11) || + (%damageType == 13) || + (%damageType > 15 && %damageType < 24) || + (%damageType > 25 && %damageType < 32)) + { + messageClient(%client, 'MsgClientHit', %client.playerHitWav); + } + } + } + // ------------------------------------------------------------------ + + //call the game specific AI routines... + if (isObject(%clVictim) && %clVictim.isAIControlled()) + %game.onAIDamaged(%clVictim, %clAttacker, %damageType, %sourceObject); + if (isObject(%clAttacker) && %clAttacker.isAIControlled()) + %game.onAIFriendlyFire(%clVictim, %clAttacker, %damageType, %sourceObject); +} + +function DefaultGame::friendlyFireMessage(%game, %damaged, %damager) +{ + messageClient(%damaged, 'MsgDamagedByTeam', '\c1You were harmed by teammate %1', %damager.name); + messageClient(%damager, 'MsgDamagedTeam', '\c1You just harmed teammate %1.', %damaged.name); +} + +function DefaultGame::clearWaitRespawn(%game, %client) +{ + %client.waitRespawn = 0; +} + +// called from player scripts +function DefaultGame::onClientKilled(%game, %clVictim, %clKiller, %damageType, %implement, %damageLocation) +{ + // z0dd - ZOD, 5/27/03. Decrease the armors teamcount + if(!$Host::TournamentMode && $LimitArmors) + $TeamArmorCount[%clVictim.team, %clVictim.armor]--; + + %plVictim = %clVictim.player; + %plKiller = %clKiller.player; + %clVictim.plyrPointOfDeath = %plVictim.position; + %clVictim.plyrDiedHoldingFlag = %plVictim.holdingFlag; + %clVictim.waitRespawn = 1; + + cancel(%plVictim.reloadSchedule); // z0dd - ZOD, 9/3/02. Cancel rapid wpn fire fix scheduler + cancel(%plVictim.reCloak); + cancel(%clVictim.respawnTimer); + %clVictim.respawnTimer = %game.schedule(($Host::PlayerRespawnTimeout * 1000), "forceObserver", %clVictim, "spawnTimeout" ); + + // reset the alarm for out of bounds + if(%clVictim.outOfBounds) { + messageClient(%clVictim, 'EnterMissionArea', ""); + %clVictim.outOfBounds = false; // z0dd - ZOD, 5/19/03. Clear the var as well + } + + // ---------------------------------------------------- + // z0dd - ZOD, 7/13/02. Respawn time reduced. Was 10 + //if (%damageType == $DamageType::suicide) + //%respawnDelay = 10; + //else + %respawnDelay = 2; + // ---------------------------------------------------- + + %game.schedule(%respawnDelay*1000, "clearWaitRespawn", %clVictim); + // if victim had an undetonated satchel charge pack, get rid of it + if(%plVictim.thrownChargeId != 0) + if(!%plVictim.thrownChargeId.kaboom) + %plVictim.thrownChargeId.delete(); + + if(%plVictim.lastVehicle !$= "") + { + schedule(15000, %plVictim.lastVehicle,"vehicleAbandonTimeOut", %plVictim.lastVehicle); + %plVictim.lastVehicle.lastPilot = ""; + } + + // unmount pilot or remove sight from bomber + if(%plVictim.isMounted()) + { + if(%plVictim.vehicleTurret) + %plVictim.vehicleTurret.getDataBlock().playerDismount(%plVictim.vehicleTurret); + else + { + %plVictim.getDataBlock().doDismount(%plVictim, true); + %plVictim.mountVehicle = false; + } + } + + if(%plVictim.inStation) + commandToClient(%plVictim.client,'setStationKeys', false); + %clVictim.camera.mode = "playerDeath"; + + // reset who triggered this station and cancel outstanding armor switch thread + if(%plVictim.station) + { + %plVictim.station.triggeredBy = ""; + %plVictim.station.getDataBlock().stationTriggered(%plVictim.station,0); + if(%plVictim.armorSwitchSchedule) + cancel(%plVictim.armorSwitchSchedule); + } + + //Close huds if player dies... + messageClient(%clVictim, 'CloseHud', "", 'inventoryScreen'); + messageClient(%clVictim, 'CloseHud', "", 'vehicleHud'); + commandToClient(%clVictim, 'setHudMode', 'Standard', "", 0); + + // $weaponslot from item.cs + %plVictim.setRepairRate(0); + %plVictim.setImageTrigger($WeaponSlot, false); + + playDeathAnimation(%plVictim, %damageLocation, %damageType); + playDeathCry(%plVictim); + + %victimName = %clVictim.name; + + %game.displayDeathMessages(%clVictim, %clKiller, %damageType, %implement); + %game.updateKillScores(%clVictim, %clKiller, %damageType, %implement); + + // toss whatever is being carried, '$flagslot' from item.cs + // MES - had to move this to after death message display because of Rabbit game type + for(%index = 0 ; %index < 8; %index++) + { + %image = %plVictim.getMountedImage(%index); + if(%image) + { + if(%index == $FlagSlot) + %plVictim.throwObject(%plVictim.holdingFlag); + else + %plVictim.throw(%image.item); + } + } + + // target manager update + setTargetDataBlock(%clVictim.target, 0); + setTargetSensorData(%clVictim.target, 0); + + // clear the hud + %clVictim.SetWeaponsHudClearAll(); + %clVictim.SetInventoryHudClearAll(); + %clVictim.setAmmoHudCount(-1); + + // clear out weapons, inventory and pack huds + messageClient(%clVictim, 'msgDeploySensorOff', ""); //make sure the deploy hud gets shut off + messageClient(%clVictim, 'msgPackIconOff', ""); // clear the pack icon + + //clear the deployable HUD + %plVictim.client.deployPack = false; + cancel(%plVictim.deployCheckThread); + deactivateDeploySensor(%plVictim); + + //if the killer was an AI... + if (isObject(%clKiller) && %clKiller.isAIControlled()) + %game.onAIKilledClient(%clVictim, %clKiller, %damageType, %implement); + + + // reset control object on this player: also sets 'playgui' as content + serverCmdResetControlObject(%clVictim); + + // set control object to the camera + %clVictim.player = 0; + %transform = %plVictim.getTransform(); + + //note, AI's don't have a camera... + if (isObject(%clVictim.camera)) + { + %clVictim.camera.setTransform(%transform); + %clVictim.camera.setOrbitMode(%plVictim, %plVictim.getTransform(), 0.5, 4.5, 4.5); + %clVictim.setControlObject(%clVictim.camera); + } + + //hook in the AI specific code for when a client dies + if (%clVictim.isAIControlled()) + { + aiReleaseHumanControl(%clVictim.controlByHuman, %clVictim); + %game.onAIKilled(%clVictim, %clKiller, %damageType, %implement); + } + else + aiReleaseHumanControl(%clVictim, %clVictim.controlAI); + + //used to track corpses so the AI can get ammo, etc... + AICorpseAdded(%plVictim); + + //if the death was a suicide, prevent respawning for 5 seconds... + %clVictim.lastDeathSuicide = false; + if (%damageType == $DamageType::Suicide) + { + %clVictim.lastDeathSuicide = true; + %clVictim.suicideRespawnTime = getSimTime() + 1000; // z0dd - ZOD, 4/24/02. Respawn time reduced. Was 5000 + } +} + +function DefaultGame::forceObserver( %game, %client, %reason ) +{ + //make sure we have a valid client... + if (%client <= 0) + return; + + // first kill this player + if(%client.player) + %client.player.scriptKill(0); + + if( %client.respawnTimer ) + cancel(%client.respawnTimer); + + %client.respawnTimer = ""; + + // remove them from the team rank array + %game.removeFromTeamRankArray(%client); + + // place them in observer mode + %client.lastObserverSpawn = -1; + %client.observerStartTime = getSimTime(); + %adminForce = 0; + + switch$ ( %reason ) + { + case "playerChoose": + %client.camera.getDataBlock().setMode( %client.camera, "observerFly" ); + messageClient(%client, 'MsgClientJoinTeam', '\c2You have become an observer.', %client.name, %game.getTeamName(0), %client, 0 ); + logEcho(%client.nameBase@" (cl "@%client@") entered observer mode"); + %client.lastTeam = %client.team; + + case "AdminForce": + %client.camera.getDataBlock().setMode( %client.camera, "observerFly" ); + messageClient(%client, 'MsgClientJoinTeam', '\c2You have been forced into observer mode by the admin.', %client.name, %game.getTeamName(0), %client, 0 ); + logEcho(%client.nameBase@" (cl "@%client@") was forced into observer mode by admin"); + %client.lastTeam = %client.team; + %adminForce = 1; + + if($Host::TournamentMode) + { + if(!$matchStarted) + { + if(%client.camera.Mode $= "pickingTeam") + { + commandToClient( %client, 'processPickTeam'); + clearBottomPrint( %client ); + } + else + { + clearCenterPrint(%client); + %client.notReady = true; + } + } + } + + case "spawnTimeout": + %client.camera.getDataBlock().setMode( %client.camera, "observerTimeout" ); + messageClient(%client, 'MsgClientJoinTeam', '\c2You have been placed in observer mode due to delay in respawning.', %client.name, %game.getTeamName(0), %client, 0 ); + logEcho(%client.nameBase@" (cl "@%client@") was placed in observer mode due to spawn delay"); + // save the team the player was on - only if this was a delay in respawning + %client.lastTeam = %client.team; + } + + // switch client to team 0 (observer) + %client.team = 0; + %client.player.team = 0; + setTargetSensorGroup( %client.target, %client.team ); + %client.setSensorGroup( %client.team ); + + // set their control to the obs. cam + %client.setControlObject( %client.camera ); + commandToClient(%client, 'setHudMode', 'Observer'); + + // display the hud + //displayObserverHud(%client, 0); + updateObserverFlyHud(%client); + + + // message everyone about this event + if( !%adminForce ) + messageAllExcept(%client, -1, 'MsgClientJoinTeam', '\c2%1 has become an observer.', %client.name, %game.getTeamName(0), %client, 0 ); + else + messageAllExcept(%client, -1, 'MsgClientJoinTeam', '\c2The admin has forced %1 to become an observer.', %client.name, %game.getTeamName(0), %client, 0 ); + + updateCanListenState( %client ); + + // call the onEvent for this game type + %game.onClientEnterObserverMode(%client); //Bounty uses this to remove this client from others' hit lists +} + +function DefaultGame::displayDeathMessages(%game, %clVictim, %clKiller, %damageType, %implement) +{ + // ---------------------------------------------------------------------------------- + // z0dd - ZOD, 6/18/02. From Panama Jack, send the damageTypeText as the last varible + // in each death message so client knows what weapon it was that killed them. + + %victimGender = (%clVictim.sex $= "Male" ? 'him' : 'her'); + %victimPoss = (%clVictim.sex $= "Male" ? 'his' : 'her'); + %killerGender = (%clKiller.sex $= "Male" ? 'him' : 'her'); + %killerPoss = (%clKiller.sex $= "Male" ? 'his' : 'her'); + %victimName = %clVictim.name; + %killerName = %clKiller.name; + //error("DamageType = " @ %damageType @ ", implement = " @ %implement @ ", implement class = " @ %implement.getClassName() @ ", is controlled = " @ %implement.getControllingClient()); + + if(%damageType == $DamageType::Explosion) + { + messageAll('msgExplosionKill', $DeathMessageExplosion[mFloor(getRandom() * $DeathMessageExplosionCount)], %victimName, %victimGender, %victimPoss, %killerName, %killerGender, %killerPoss, %damageType, $DamageTypeText[%damageType]); + logEcho(%clVictim.nameBase@" (pl "@%clVictim.player@"/cl "@%clVictim@") killed by a nearby explosion."); + } + else if(%damageType == $DamageType::Suicide) //player presses cntrl-k + { + messageAll('msgSuicide', $DeathMessageSuicide[mFloor(getRandom() * $DeathMessageSuicideCount)], %victimName, %victimGender, %victimPoss, %killerName, %killerGender, %killerPoss, %damageType, $DamageTypeText[%damageType]); + logEcho(%clVictim.nameBase@" (pl "@%clVictim.player@"/cl "@%clVictim@") committed suicide (CTRL-K)"); + } + else if(%damageType == $DamageType::VehicleSpawn) + { + messageAll('msgVehicleSpawnKill', $DeathMessageVehPad[mFloor(getRandom() * $DeathMessageVehPadCount)], %victimName, %victimGender, %victimPoss, %killerName, %killerGender, %killerPoss, %damageType, $DamageTypeText[%damageType]); + logEcho(%clVictim.nameBase@" (pl "@%clVictim.player@"/cl "@%clVictim@") killed by vehicle spawn"); + } + else if(%damageType == $DamageType::ForceFieldPowerup) + { + messageAll('msgVehicleSpawnKill', $DeathMessageFFPowerup[mFloor(getRandom() * $DeathMessageFFPowerupCount)], %victimName, %victimGender, %victimPoss, %killerName, %killerGender, %killerPoss, %damageType, $DamageTypeText[%damageType]); + logEcho(%clVictim.nameBase@" (pl "@%clVictim.player@"/cl "@%clVictim@") killed by Force Field Powerup"); + } + else if(%damageType == $DamageType::Crash) + { + messageAll('msgVehicleCrash', $DeathMessageVehicleCrash[%damageType, mFloor(getRandom() * $DeathMessageVehicleCrashCount)], %victimName, %victimGender, %victimPoss, %killerName, %killerGender, %killerPoss, %damageType, $DamageTypeText[%damageType]); + logEcho(%clVictim.nameBase@" (pl "@%clVictim.player@"/cl "@%clVictim@") crashes a vehicle."); + } + else if(%damageType == $DamageType::Impact) // run down by vehicle + { + if( ( %controller = %implement.getControllingClient() ) > 0) + { + %killerGender = (%controller.sex $= "Male" ? 'him' : 'her'); + %killerPoss = (%controller.sex $= "Male" ? 'his' : 'her'); + %killerName = %controller.name; + messageAll('msgVehicleKill', $DeathMessageVehicle[mFloor(getRandom() * $DeathMessageVehicleCount)], %victimName, %victimGender, %victimPoss, %killerName ,%killerGender, %killerPoss, %damageType, $DamageTypeText[%damageType]); + logEcho(%clVictim.nameBase@" (pl "@%clVictim.player@"/cl "@%clVictim@") killed by a vehicle controlled by "@%controller); + } + else + { + messageAll('msgVehicleKill', $DeathMessageVehicleUnmanned[mFloor(getRandom() * $DeathMessageVehicleUnmannedCount)], %victimName, %victimGender, %victimPoss, %killerName, %killerGender, %killerPoss, %damageType, $DamageTypeText[%damageType]); + logEcho(%clVictim.nameBase@" (pl "@%clVictim.player@"/cl "@%clVictim@") killed by a vehicle (unmanned)"); + } + } + // --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + // z0dd - ZOD, 5/15/02. Added Hover Vehicle so we get proper + // death messages when killed with Wildcat chaingun + //else if (isObject(%implement) && (%implement.getClassName() $= "Turret" || %implement.getClassName() $= "VehicleTurret" || %implement.getClassName() $= "FlyingVehicle")) //player killed by a turret + else if (isObject(%implement) && (%implement.getClassName() $= "Turret" || %implement.getClassName() $= "VehicleTurret" || %implement.getClassName() $= "FlyingVehicle" || %implement.getClassName() $= "HoverVehicle")) + { + if (%implement.getControllingClient() != 0) //is turret being controlled? + { + %controller = %implement.getControllingClient(); + %killerGender = (%controller.sex $= "Male" ? 'him' : 'her'); + %killerPoss = (%controller.sex $= "Male" ? 'his' : 'her'); + %killerName = %controller.name; + + if (%controller == %clVictim) + messageAll('msgTurretSelfKill', $DeathMessageTurretSelfKill[mFloor(getRandom() * $DeathMessageTurretSelfKillCount)],%victimName, %victimGender, %victimPoss, %killerName, %killerGender, %killerPoss, %damageType, $DamageTypeText[%damageType]); + else if (%controller.team == %clVictim.team) //controller TK'd a friendly + messageAll('msgCTurretKill', $DeathMessageCTurretTeamKill[%damageType, mFloor(getRandom() * $DeathMessageCTurretTeamKillCount)],%victimName, %victimGender, %victimPoss, %killerName, %killerGender, %killerPoss, %damageType, $DamageTypeText[%damageType]); + else //controller killed an enemy + messageAll('msgCTurretKill', $DeathMessageCTurretKill[%damageType, mFloor(getRandom() * $DeathMessageCTurretKillCount)],%victimName, %victimGender, %victimPoss, %killerName, %killerGender, %killerPoss, %damageType, $DamageTypeText[%damageType]); + logEcho(%clVictim.nameBase@" (pl "@%clVictim.player@"/cl "@%clVictim@") killed by a turret controlled by "@%controller); + } + // use the handle associated with the deployed object to verify valid owner + else if (isObject(%implement.owner)) + { + %owner = %implement.owner; + //error("Owner is " @ %owner @ " Handle is " @ %implement.ownerHandle); + //error("Turret is still owned"); + //turret is uncontrolled, but is owned - treat the same as controlled. + %killerGender = (%owner.sex $= "Male" ? 'him' : 'her'); + %killerPoss = (%owner.sex $= "Male" ? 'his' : 'her'); + %killerName = %owner.name; + + if (%owner.team == %clVictim.team) //player got in the way of a teammates deployed but uncontrolled turret. + messageAll('msgCTurretKill', $DeathMessageCTurretAccdtlKill[%damageType,mFloor(getRandom() * $DeathMessageCTurretAccdtlKillCount)],%victimName, %victimGender, %victimPoss, %killerName, %killerGender, %killerPoss, %damageType, $DamageTypeText[%damageType]); + else //deployed, uncontrolled turret killed an enemy + messageAll('msgCTurretKill', $DeathMessageCTurretKill[%damageType,mFloor(getRandom() * $DeathMessageCTurretKillCount)],%victimName, %victimGender, %victimPoss, %killerName, %killerGender, %killerPoss, %damageType, $DamageTypeText[%damageType]); + logEcho(%clVictim.nameBase@" (pl "@%clVictim.player@"/cl "@%clVictim@") was killed by turret (automated)"); + } + else //turret is not a placed (owned) turret (or owner is no longer on it's team), and is not being controlled + { + messageAll('msgTurretKill', $DeathMessageTurretKill[%damageType,mFloor(getRandom() * $DeathMessageTurretKillCount)],%victimName, %victimGender, %victimPoss, %killerName, %killerGender, %killerPoss, %damageType, $DamageTypeText[%damageType]); + logEcho(%clVictim.nameBase@" (pl "@%clVictim.player@"/cl "@%clVictim@") killed by turret"); + } + } + else if((%clKiller == %clVictim) || (%damageType == $DamageType::Ground)) //player killed himself or fell to death + { + messageAll('msgSelfKill', $DeathMessageSelfKill[%damageType,mFloor(getRandom() * $DeathMessageSelfKillCount)], %victimName, %victimGender, %victimPoss, %killerName, %killerGender, %killerPoss, %damageType, $DamageTypeText[%damageType]); + logEcho(%clVictim.nameBase @ "(pl " @ %clVictim.player @ "/cl " @ %clVictim @ ") killed self (" @ getTaggedString($DamageTypeText[%damageType]) @ ")"); + } + + else if (%damageType == $DamageType::OutOfBounds) //killer died due to Out-of-Bounds damage + { + messageAll('msgOOBKill', $DeathMessageOOB[mFloor(getRandom() * $DeathMessageOOBCount)], %victimName, %victimGender, %victimPoss, %killerName, %killerGender, %killerPoss, %damageType, $DamageTypeText[%damageType]); + logEcho(%clVictim.nameBase@" (pl "@%clVictim.player@"/cl "@%clVictim@") killed by out-of-bounds damage"); + } + + else if (%damageType == $DamageType::NexusCamping) //Victim died from camping near the nexus... + { + messageAll('msgCampKill', $DeathMessageCamping[mFloor(getRandom() * $DeathMessageCampingCount)], %victimName, %victimGender, %victimPoss, %killerName, %killerGender, %killerPoss, %damageType, $DamageTypeText[%damageType]); + logEcho(%clVictim.nameBase@" (pl "@%clVictim.player@"/cl "@%clVictim@") killed for nexus camping"); + } + + else if(%clKiller.team == %clVictim.team) //was a TK + { + messageAll('msgTeamKill', $DeathMessageTeamKill[%damageType, mFloor(getRandom() * $DeathMessageTeamKillCount)], %victimName, %victimGender, %victimPoss, %killerName, %killerGender, %killerPoss, %damageType, $DamageTypeText[%damageType]); + logEcho(%clVictim.nameBase@" (pl "@%clVictim.player@"/cl "@%clVictim@") teamkilled by "@%clKiller.nameBase@" (pl "@%clKiller.player@"/cl "@%clKiller@")"); + } + + else if (%damageType == $DamageType::Lava) //player died by falling in lava + { + messageAll('msgLavaKill', $DeathMessageLava[mFloor(getRandom() * $DeathMessageLavaCount)], %victimName, %victimGender, %victimPoss, %killerName, %killerGender, %killerPoss, %damageType, $DamageTypeText[%damageType]); + logEcho(%clVictim.nameBase@" (pl "@%clVictim.player@"/cl "@%clVictim@") killed by lava"); + } + else if ( %damageType == $DamageType::Lightning ) // player was struck by lightning + { + messageAll('msgLightningKill', $DeathMessageLightning[mFloor(getRandom() * $DeathMessageLightningCount)], %victimName, %victimGender, %victimPoss, %killerName, %killerGender, %killerPoss, %damageType, $DamageTypeText[%damageType]); + logEcho(%clVictim.nameBase@" (pl "@%clVictim.player@"/cl "@%clVictim@") killed by lightning"); + } + else if ( %damageType == $DamageType::Mine && !isObject(%clKiller) ) + { + error("Mine kill w/o source"); + messageAll('MsgRogueMineKill', $DeathMessageRogueMine[%damageType, mFloor(getRandom() * $DeathMessageRogueMineCount)], %victimName, %victimGender, %victimPoss, %killerName, %killerGender, %killerPoss, %damageType, $DamageTypeText[%damageType]); + } + else //was a legitimate enemy kill + { + if(%damageType == 6 && (%clVictim.headShot)) + { + // laser headshot just occurred + messageAll('MsgHeadshotKill', $DeathMessageHeadshot[%damageType, mFloor(getRandom() * $DeathMessageHeadshotCount)], %victimName, %victimGender, %victimPoss, %killerName, %killerGender, %killerPoss, %damageType, $DamageTypeText[%damageType]); + } + // ---------------------------------------------------- + // z0dd - ZOD, 8/25/02. Rear Lance hits + else if (%damageType == 10 && (%clVictim.rearshot)) + { + // shocklance rearshot just occurred + messageAll('MsgRearshotKill', $DeathMessageRearshot[%damageType, mFloor(getRandom() * $DeathMessageRearshotCount)], %victimName, %victimGender, %victimPoss, %killerName, %killerGender, %killerPoss, %damageType, $DamageTypeText[%damageType]); + } + // ---------------------------------------------------- + else + messageAll('MsgLegitKill', $DeathMessage[%damageType, mFloor(getRandom() * $DeathMessageCount)], %victimName, %victimGender, %victimPoss, %killerName, %killerGender, %killerPoss, %damageType, $DamageTypeText[%damageType]); + logEcho(%clVictim.nameBase @ " (pl " @ %clVictim.player @ "/cl " @ %clVictim @ ") killed by " @ %clKiller.nameBase @ " (pl " @ %clKiller.player @ "/cl " @%clKiller @ ") using " @ getTaggedString($DamageTypeText[%damageType])); + } +} + +function DefaultGame::assignClientTeam(%game, %client, %respawn ) +{ +//error("DefaultGame::assignClientTeam"); + // this function is overwritten in non-team mission types (e.g. DM) + // so these lines won't do anything + //if(!%game.numTeams) + //{ + // setTargetSkin(%client.target, %client.skin); + // return; + //} + + // camera is responsible for creating a player + // - counts the number of players per team + // - puts this player on the least player count team + // - sets the client's skin to the servers default + + %numPlayers = ClientGroup.getCount(); + for(%i = 0; %i <= %game.numTeams; %i++) + %numTeamPlayers[%i] = 0; + + for(%i = 0; %i < %numPlayers; %i = %i + 1) + { + %cl = ClientGroup.getObject(%i); + if(%cl != %client) + %numTeamPlayers[%cl.team]++; + } + %leastPlayers = %numTeamPlayers[1]; + %leastTeam = 1; + for(%i = 2; %i <= %game.numTeams; %i++) + { + if( (%numTeamPlayers[%i] < %leastPlayers) || + ( (%numTeamPlayers[%i] == %leastPlayers) && + ($teamScore[%i] < $teamScore[%leastTeam] ) )) + { + %leastTeam = %i; + %leastPlayers = %numTeamPlayers[%i]; + } + } + + %client.team = %leastTeam; + %client.lastTeam = %team; + + // Assign the team skin: + if ( %client.isAIControlled() ) + { + if ( %leastTeam & 1 ) + { + %client.skin = addTaggedString( "basebot" ); + setTargetSkin( %client.target, 'basebot' ); + } + else + { + %client.skin = addTaggedString( "basebbot" ); + setTargetSkin( %client.target, 'basebbot' ); + } + } + else + setTargetSkin( %client.target, %game.getTeamSkin(%client.team) ); + //setTargetSkin( %client.target, %client.skin ); + + // might as well standardize the messages + //messageAllExcept( %client, -1, 'MsgClientJoinTeam', '\c1%1 joined %2.', %client.name, $teamName[%leastTeam], %client, %leastTeam ); + //messageClient( %client, 'MsgClientJoinTeam', '\c1You joined the %2 team.', $client.name, $teamName[%client.team], %client, %client.team ); + messageAllExcept( %client, -1, 'MsgClientJoinTeam', '\c1%1 joined %2.', %client.name, %game.getTeamName(%client.team), %client, %client.team ); + messageClient( %client, 'MsgClientJoinTeam', '\c1You joined the %2 team.', %client.name, %game.getTeamName(%client.team), %client, %client.team ); + + updateCanListenState( %client ); + + // zodd - ZOD, 5/27/03. Setup armor max counts + countArmorAllowed(); + + logEcho(%client.nameBase@" (cl "@%client@") joined team "@%client.team); + +} + +function DefaultGame::getTeamSkin(%game, %team) +{ + //error("DefaultGame::getTeamSkin"); + %skin = $teamSkin[%team]; + //error("%skin = " SPC getTaggedString(%skin)); + return %skin; +} + +function DefaultGame::getTeamName(%game, %team) +{ + //error("DefaultGame::getTeamName"); + %name = $teamName[%team]; + //error("name = " SPC getTaggedString(%name)); + return %name; +} + +function DefaultGame::clientJoinTeam( %game, %client, %team, %respawn ) +{ +//error("DefaultGame::clientJoinTeam"); + if ( %team < 1 || %team > %game.numTeams ) + return; + + if( %respawn $= "" ) + %respawn = 1; + + %client.team = %team; + %client.lastTeam = %team; + setTargetSkin( %client.target, %game.getTeamSkin(%team) ); + setTargetSensorGroup( %client.target, %team ); + %client.setSensorGroup( %team ); + + // Spawn the player: + %game.spawnPlayer( %client, %respawn ); + + messageAllExcept( %client, -1, 'MsgClientJoinTeam', '\c1%1 joined %2.', %client.name, %game.getTeamName(%team), %client, %team ); + messageClient( %client, 'MsgClientJoinTeam', '\c1You joined the %2 team.', $client.name, %game.getTeamName(%client.team), %client, %client.team ); + + updateCanListenState( %client ); + + logEcho(%client.nameBase@" (cl "@%client@") joined team "@%client.team); + +} + +function DefaultGame::AIHasJoined(%game, %client) +{ + //defined to prevent console spam +} + +function DefaultGame::AIChangeTeam(%game, %client, %newTeam) +{ + //make sure we're trying to drop an AI + if (!isObject(%client) || !%client.isAIControlled()) + return; + + //clear the ai from any objectives, etc... + AIUnassignClient(%client); + %client.stop(); + %client.clearTasks(); + %client.clearStep(); + %client.lastDamageClient = -1; + %client.lastDamageTurret = -1; + %client.shouldEngage = -1; + %client.setEngageTarget(-1); + %client.setTargetObject(-1); + %client.pilotVehicle = false; + %client.defaultTasksAdded = false; + + //kill the player, which should cause the Game object to perform whatever cleanup is required. + if (isObject(%client.player)) + %client.player.scriptKill(0); + + //clean up the team rank array + %game.removeFromTeamRankArray(%client); + + //assign the new team + %client.team = %newTeam; + if (%newTeam < 0) + Game.assignClientTeam(%client); + else + { + if ( %client.team & 1 ) + { + %client.skin = addTaggedString( "basebot" ); + setTargetSkin( %client.target, 'basebot' ); + } + else + { + %client.skin = addTaggedString( "basebbot" ); + setTargetSkin( %client.target, 'basebbot' ); + } + } + + messageAllExcept( %client, -1, 'MsgClientJoinTeam', '\c1bot %1 has switched to team %2.', %client.name, %game.getTeamName(%client.team), %client, %client.team ); + +} + +function DefaultGame::clientChangeTeam(%game, %client, %team, %fromObs, %respawned) // z0dd - ZOD, 6/06/02. Don't send a message if player used respawn feature. Added %respawned +{ +//error("DefaultGame::clientChangeTeam"); + //first, remove the client from the team rank array + //the player will be added to the new team array as soon as he respawns... + %game.removeFromTeamRankArray(%client); + + %pl = %client.player; + if(isObject(%pl)) + { + if(%pl.isMounted()) + %pl.getDataBlock().doDismount(%pl); + %pl.scriptKill(0); + } + + // reset the client's targets and tasks only + clientResetTargets(%client, true); + + // give this client a new handle to disassociate ownership of deployed objects + if( %team $= "" && (%team > 0 && %team <= %game.numTeams)) + { + if( %client.team == 1 ) + %client.team = 2; + else + %client.team = 1; + } + else + %client.team = %team; + + // Set the client's skin: + if (!%client.isAIControlled()) + setTargetSkin( %client.target, %game.getTeamSkin(%client.team) ); + setTargetSensorGroup( %client.target, %client.team ); + %client.setSensorGroup( %client.team ); + + // Spawn the player: + %client.lastSpawnPoint = %game.pickPlayerSpawn( %client ); + + %game.createPlayer( %client, %client.lastSpawnPoint, $MatchStarted ); + + if($MatchStarted) + %client.setControlObject(%client.player); + else + { + %client.camera.getDataBlock().setMode(%client.camera, "pre-game", %client.player); + %client.setControlObject(%client.camera); + } + + // call the onEvent for this game type + %game.onClientEnterObserverMode(%client); //Bounty uses this to remove this client from others' hit lists + + if(%fromObs $= "" || !%fromObs) + { + //------------------------------------------------------------------------- + // z0dd - ZOD, 6/06/02. Don't send a message if player used respawn feature + if(!%respawned) + { + messageAllExcept( %client, -1, 'MsgClientJoinTeam', '\c1%1 switched to team %2.', %client.name, %game.getTeamName(%client.team), %client, %client.team ); + messageClient( %client, 'MsgClientJoinTeam', '\c1You switched to team %2.', $client.name, %game.getTeamName(%client.team), %client, %client.team ); + } + //------------------------------------------------------------------------- + } + else + { + messageAllExcept( %client, -1, 'MsgClientJoinTeam', '\c1%1 joined team %2.', %client.name, %game.getTeamName(%client.team), %client, %team ); + messageClient( %client, 'MsgClientJoinTeam', '\c1You joined team %2.', $client.name, %game.getTeamName(%client.team), %client, %client.team ); + } + + updateCanListenState( %client ); + + // MES - switch objective hud lines when client switches teams + messageClient(%client, 'MsgCheckTeamLines', "", %client.team); + logEcho(%client.nameBase@" (cl "@%client@") switched to team "@%client.team); +} + +// missioncleanup and missiongroup are checked prior to entering game code +function DefaultGame::missionLoadDone(%game) +{ + // walks through the mission group and sets the power stuff up + // - groups get initialized with power count 0 then iterated to + // increment powercount if an object within is powered + // - powers objects up/down + //MissionGroup.objectiveInit(); + MissionGroup.clearPower(); + MissionGroup.powerInit(0); + + %game.initGameVars(); //set up scoring variables and other game specific globals + + // make team0 visible/friendly to all + setSensorGroupAlwaysVisMask(0, 0xffffffff); + setSensorGroupFriendlyMask(0, 0xffffffff); + + // update colors: + // - enemy teams are red + // - same team is green + // - team 0 is white + for(%i = 0; %i < 32; %i++) + { + %team = (1 << %i); + setSensorGroupColor(%i, %team, "0 255 0 255"); + setSensorGroupColor(%i, ~%team, "255 0 0 255"); + setSensorGroupColor(%i, 1, "255 255 255 255"); + + // setup the team targets (alwyas friendly and visible to same team) + setTargetAlwaysVisMask(%i, %team); + setTargetFriendlyMask(%i, %team); + } + + //set up the teams + %game.setUpTeams(); + + //clear out the team rank array... + for (%i = 0; %i < 32; %i++) + $TeamRank[%i, count] = ""; + + // objectiveInit has to take place after setupTeams -- objective HUD relies on flags + // having their team set + MissionGroup.objectiveInit(); + + //initialize the AI system + %game.aiInit(); + + //need to reset the teams if we switch from say, CTF to Bounty... + // assign the bots team + if ($currentMissionType !$= $previousMissionType) + { + $previousMissionType = $currentMissionType; + for(%i = 0; %i < ClientGroup.getCount(); %i++) + { + %cl = ClientGroup.getObject(%i); + if (%cl.isAIControlled()) + %game.assignClientTeam(%cl); + } + } + + //Save off respawn or Siege Team switch information... + if(%game.class !$= "SiegeGame") + MissionGroup.setupPositionMarkers(true); + echo("Default game mission load done."); + +} + +function DefaultGame::onClientLeaveGame(%game, %client) +{ + // if there is a player attached to this client, kill it + if( isObject(%client.player)) + %client.player.scriptKill(0); + + //cancel a scheduled call... + cancel(%client.respawnTimer); + %client.respawnTimer = ""; + + //remove them from the team rank arrays + %game.removeFromTeamRankArray(%client); + logEcho(%client.nameBase@" (cl "@%client@") dropped"); +} + +function DefaultGame::clientMissionDropReady(%game, %client) +{ + //synchronize the clock HUD + messageClient(%client, 'MsgSystemClock', "", 0, 0); + + %game.sendClientTeamList( %client ); + %game.setupClientHuds( %client ); + + if($CurrentMissionType $= "SinglePlayer") + { + //CommandToClient( %client, 'setPlayContent'); + return; + } + + %observer = false; + if( !$Host::TournamentMode ) + { + if( %client.camera.mode $= "observerFly" || %client.camera.mode $= "justJoined") + { + %observer = true; + %client.observerStartTime = getSimTime(); + commandToClient(%client, 'setHudMode', 'Observer'); + %client.setControlObject( %client.camera ); + //displayObserverHud( %client, 0 ); + updateObserverFlyHud(%client); + } + + if( !%observer ) + { + if(!$MatchStarted && !$CountdownStarted) // server has not started anything yet + { + %client.setControlObject( %client.camera ); + commandToClient(%client, 'setHudMode', 'Observer'); + } + else if(!$MatchStarted && $CountdownStarted) // server has started the countdown + { + commandToClient(%client, 'setHudMode', 'Observer'); + %client.setControlObject( %client.camera ); + } + else + { + commandToClient(%client, 'setHudMode', 'Standard'); // the game has already started + %client.setControlObject( %client.player ); + } + } + } + else + { + // set all players into obs mode. setting the control object will handle further procedures... + %client.camera.getDataBlock().setMode( %client.camera, "ObserverFly" ); + commandToClient(%client, 'setHudMode', 'Observer'); + %client.setControlObject( %client.camera ); + messageAll( 'MsgClientJoinTeam', "",%client.name, $teamName[0], %client, 0 ); + %client.team = 0; + + if( !$MatchStarted && !$CountdownStarted) + { + if($TeamDamage) + %damMess = "ENABLED"; + else + %damMess = "DISABLED"; + + if(%game.numTeams > 1) + BottomPrint(%client, "Server is Running in Tournament Mode.\nPick a Team\nTeam Damage is " @ %damMess, 0, 3 ); + } + else + { + BottomPrint( %client, "\nServer is Running in Tournament Mode", 0, 3 ); + } + } + + //make sure the objective HUD indicates your team on top and in green... + if (%client.team > 0) + messageClient(%client, 'MsgCheckTeamLines', "", %client.team); + + // were ready to go. + %client.matchStartReady = true; + echo("Client" SPC %client SPC "is ready."); + + // z0dd - ZOD, 9/29/02. Removed T2 demo code from here +} + +function DefaultGame::sendClientTeamList(%game, %client) +{ + // Send the client the current team list: + %teamCount = %game.numTeams; + for ( %i = 0; %i < %teamCount; %i++ ) + { + if ( %i > 0 ) + %teamList = %teamList @ "\n"; + + %teamList = %teamList @ detag( getTaggedString( %game.getTeamName(%i + 1) ) ); + } + messageClient( %client, 'MsgTeamList', "", %teamCount, %teamList ); +} + +function DefaultGame::setupClientHuds(%game, %client) +{ + // tell the client to setup the huds... + for(%i =0; %i<$WeaponsHudCount; %i++) + %client.setWeaponsHudBitmap(%i, $WeaponsHudData[%i, itemDataName], $WeaponsHudData[%i, bitmapName]); + for(%i =0; %i<$InventoryHudCount; %i++) + { + if ( $InventoryHudData[%i, slot] != 0 ) + %client.setInventoryHudBitmap($InventoryHudData[%i, slot], $InventoryHudData[%i, itemDataName], $InventoryHudData[%i, bitmapName]); + } + %client.setInventoryHudBitmap( 0, "", "gui/hud_handgren" ); + + %client.setWeaponsHudBackGroundBmp("gui/hud_new_panel"); + %client.setWeaponsHudHighLightBmp("gui/hud_new_weaponselect"); + %client.setWeaponsHudInfiniteAmmoBmp("gui/hud_infinity"); + %client.setInventoryHudBackGroundBmp("gui/hud_new_panel"); + + // tell the client if we are protecting statics (so no health bar will be displayed) + commandToClient(%client, 'protectingStaticObjects', %game.allowsProtectedStatics()); + commandToClient(%client, 'setPowerAudioProfiles', sPowerUp.getId(), sPowerDown.getId()); +} + +function DefaultGame::testDrop( %game, %client ) +{ + %game.clientJoinTeam( %client, 1, false ); + %client.camera.getDataBlock().setMode( %client.camera, "pre-game", %client.player ); + %client.setControlObject( %client.camera ); + CommandToClient( %client, 'setPlayContent' ); +} + +function DefaultGame::onClientEnterObserverMode( %game, %client ) +{ + // Default game doesn't care... +} + +// from 'item.cs' +function DefaultGame::playerTouchFlag(%game, %player, %flag) +{ + messageAll('MsgPlayerTouchFlag', 'Player %1 touched flag %2', %player, %flag); +} + +// from 'item.cs' +function DefaultGame::playerDroppedFlag(%game, %player, %flag) +{ + messageAll('MsgPlayerDroppedFlag', 'Player %1 dropped flag %2', %player, %flag); +} + +// from 'staticShape.cs' +function DefaultGame::flagStandCollision(%game, %dataBlock, %obj, %colObj) +{ + // for retreiveGame +} + +function DefaultGame::notifyMineDeployed(%game, %mine) +{ + //do nothign in the default game... +} + +// from 'staticshape.cs' +function DefaultGame::findProjector(%game, %flipflop) +{ + // search the flipflop's folder for a holo projector + // if one exists, associate it with the flipflop + %flipflop.projector = 0; + %folder = %flipflop.getGroup(); + for(%i = 0; %i < %folder.getCount(); %i++) + { + %proj = %folder.getObject(%i); + if(%proj.getDatablock().getName() $= "LogoProjector") + { + %flipflop.projector = %proj; + %flipflop.projector.holo = 0; + break; + } + } +} + +//****************************************************************************** +//* DefaultGame Trigger - Functions * +//****************************************************************************** + +/// -Trigger- ////////////////////////////////////////////////////////////////// +//Function -- onEnterTrigger (%game, %name, %data, %obj, %colObj) +// %game = Current game type object +// %name = Trigger name - defined when trigger is created +// %data = Trigger Data Block +// %obj = Trigger Object +// %colObj = Object that collided with the trigger +//Decription -- Called when trigger has been triggered +//////////////////////////////////////////////////////////////////////////////// +// from 'trigger.cs' +function DefaultGame::onEnterTrigger(%game, %triggerName, %data, %obj, %colobj) +{ + //Do Nothing +} + +/// -Trigger- ////////////////////////////////////////////////////////////////// +//Function -- onLeaveTrigger (%game, %name, %data, %obj, %colObj) +// %game = Current game type object +// %name = Trigger name - defined when trigger is created +// %data = Trigger Data Block +// %obj = Trigger Object +// %colObj = Object that collided with the trigger +//Decription -- Called when trigger has been untriggered +//////////////////////////////////////////////////////////////////////////////// +// from 'trigger.cs' +function DefaultGame::onLeaveTrigger(%game, %triggerName, %data, %obj, %colobj) +{ + //Do Nothing +} + +/// -Trigger- ////////////////////////////////////////////////////////////////// +//Function -- onTickTrigger(%game, %name, %data, %obj) +// %game = Current game type object +// %name = Trigger name - defined when trigger is created +// %data = Trigger Data Block +// %obj = Trigger Object +//Decription -- Called every tick if triggered +//////////////////////////////////////////////////////////////////////////////// +// from 'trigger.cs' +function DefaultGame::onTickTrigger(%game, %triggerName, %data, %obj) +{ + //Do Nothing +} + + +function DefaultGame::setUpTeams(%game) +{ + %group = nameToID("MissionGroup/Teams"); + if(%group == -1) + return; + + // create a team0 if it does not exist + %team = nameToID("MissionGroup/Teams/team0"); + if(%team == -1) + { + %team = new SimGroup("team0"); + %group.add(%team); + } + + // 'team0' is not counted as a team here + %game.numTeams = 0; + while(%team != -1) + { + // create drop set and add all spawnsphere objects into it + %dropSet = new SimSet("TeamDrops" @ %game.numTeams); + MissionCleanup.add(%dropSet); + + %spawns = nameToID("MissionGroup/Teams/team" @ %game.numTeams @ "/SpawnSpheres"); + if(%spawns != -1) + { + %count = %spawns.getCount(); + for(%i = 0; %i < %count; %i++) + %dropSet.add(%spawns.getObject(%i)); + } + + // set the 'team' field for all the objects in this team + %team.setTeam(%game.numTeams); + + clearVehicleCount(%team+1); + // get next group + %team = nameToID("MissionGroup/Teams/team" @ %game.numTeams + 1); + if (%team != -1) + %game.numTeams++; + } + + // set the number of sensor groups (including team0) that are processed + setSensorGroupCount(%game.numTeams + 1); +} + +function SimGroup::setTeam(%this, %team) +{ + for (%i = 0; %i < %this.getCount(); %i++) + { + %obj = %this.getObject(%i); + switch$ (%obj.getClassName()) + { + case SpawnSphere : + if($MatchStarted) + { + // find out what team the spawnsphere used to belong to + %found = false; + for(%l = 1; %l <= Game.numTeams; %l++) + { + %drops = nameToId("MissionCleanup/TeamDrops" @ %l); + for(%j = 0; %j < %drops.getCount(); %j++) + { + %current = %drops.getObject(%j); + if(%current == %obj) + %found = %l; + } + } + if(%team != %found) + Game.claimSpawn(%obj, %team, %found); + else + error("spawn "@%obj@" is already on team "@%team@"!"); + } + else + Game.claimSpawn(%obj, %team, ""); + case SimGroup : %obj.setTeam(%team); + default : %obj.team = %team; + } + + if(%obj.getType() & $TypeMasks::GameBaseObjectType) + { + // eeck.. please go away when scripts get cleaned... +// if(%obj.getDataBlock().getName() $= "StationVehiclePad") +// { + // z0dd - ZOD, 4/24/02. Rewrite for bug fix and MPB teleporter +// %team = %obj.team; +// %obj = %obj.station; +// %obj.team = %team; +// setTargetSensorGroup(%obj.getTarget(), %team); +// if(%obj.teleporter !$= "") +// { +// %obj = %obj.teleporter; +// %obj.team = %team; +// } +// } + %target = %obj.getTarget(); + if(%target != -1) + setTargetSensorGroup(%target, %team); + } + } +} + +function DefaultGame::claimSpawn(%game, %obj, %newTeam, %oldTeam) +{ + if(%newTeam == %oldTeam) + return; + + %newSpawnGroup = nameToId("MissionCleanup/TeamDrops" @ %newTeam); + if(%oldTeam !$= "") + { + %oldSpawnGroup = nameToId("MissionCleanup/TeamDrops" @ %oldTeam); + %oldSpawnGroup.remove(%obj); + } + %newSpawnGroup.add(%obj); +} + +// recursive function to assign teams to all mission objects + +function SimGroup::swapTeams(%this) +{ + // used in Siege only + Game.groupSwapTeams(%this); +} + +function ShapeBase::swapTeams(%this) +{ + // used in Siege only + Game.objectSwapTeams(%this); +} + +function GameBase::swapTeams(%this) +{ + // used in Siege only + Game.objectSwapTeams(%this); +} + +function TSStatic::swapTeams(%this) +{ + // used in Siege only + // do nothing +} + +function InteriorInstance::swapTeams(%this) +{ + // used in Siege only + // do nothing -- interiors don't switch teams +} + +function SimGroup::swapVehiclePads(%this) +{ + // used in Siege only + Game.groupSwapVehiclePads(%this); +} + +function ShapeBase::swapVehiclePads(%this) +{ + // used in Siege only + Game.objectSwapVehiclePads(%this); +} + +function GameBase::swapVehiclePads(%this) +{ + // used in Siege only + // do nothing -- only searching for vehicle pads +} + +function InteriorInstance::swapVehiclePads(%this) +{ + // used in Siege only + // do nothing -- only searching for vehicle pads +} + +function SimSet::swapVehiclePads(%this) +{ + // used in Siege only + // do nothing -- only searching for vehicle pads +} + +function PhysicalZone::swapVehiclePads(%this) +{ + // used in Siege only + // do nothing -- only searching for vehicle pads +} + +function SimGroup::objectRestore(%this) +{ + // used in Siege only + Game.groupObjectRestore(%this); +} + +function ShapeBase::objectRestore(%object) +{ + // only used for Siege + Game.shapeObjectRestore(%object); +} + +function Turret::objectRestore(%object) +{ + // only used for Siege + Game.shapeObjectRestore(%object); +} + +function AIObjective::objectRestore(%object) +{ + // only used for Siege + // don't do anything for AI Objectives +} + +function DefaultGame::checkObjectives(%game) +{ + //any special objectives that can be met by gametype + //none for default game +} + +//--------------------------------------------------- + +function DefaultGame::checkTimeLimit(%game, %forced) +{ + // Don't add extra checks: + if ( %forced ) + cancel( %game.timeCheck ); + + // if there is no time limit, check back in a minute to see if it's been set + if(($Host::TimeLimit $= "") || $Host::TimeLimit == 0) + { + %game.timeCheck = %game.schedule(20000, "checkTimeLimit"); + return; + } + + %curTimeLeftMS = ($Host::TimeLimit * 60 * 1000) + $missionStartTime - getSimTime(); + + if (%curTimeLeftMS <= 0) + { + // time's up, put down your pencils + %game.timeLimitReached(); + } + else + { + if(%curTimeLeftMS >= 20000) + %game.timeCheck = %game.schedule(20000, "checkTimeLimit"); + else + %game.timeCheck = %game.schedule(%curTimeLeftMS + 1, "checkTimeLimit"); + + //now synchronize everyone's clock + messageAll('MsgSystemClock', "", $Host::TimeLimit, %curTimeLeftMS); + } +} + +function listplayers() +{ + for(%i = 0; %i < ClientGroup.getCount(); %i++) + { + %cl = ClientGroup.getObject(%i); + %status = ""; + if(%cl.isAiControlled()) + %status = "Bot "; + if(%cl.isSmurf) + %status = "Alias "; + if(%cl.isAdmin) + %status = %status @ "Admin "; + if(%cl.isSuperAdmin) + %status = %status @ "SuperAdmin "; + if(%status $= "") + %status = ""; + + echo("Client#: " @ %cl @ " Player#: " @ %cl.player @ " Name: " @ %cl.nameBase @ " Team: " @ %cl.team @ " Score: " @ %cl.score @ " Status: " @ %status); + } +} + +function DefaultGame::clearTeamRankArray(%game, %team) +{ + %count = $TeamRank[%team, count]; + for (%i = 0; %i < %count; %i++) + $TeamRank[%team, %i] = ""; + + $TeamRank[%team, count] = 0; +} + +function DefaultGame::populateTeamRankArray(%game, %client) +{ + //this function should be called *after* the client has been added to a team... + if (%client <= 0 || %client.team <= 0) + return; + + //find the team + if (%game.numTeams == 1) + %team = 0; + else + %team = %client.team; + + //find the number of teammates already ranked... + %count = $TeamRank[%team, count]; + if (%count $= "") + { + $TeamRank[%team, count] = 0; + %count = 0; + } + + //make sure we're not already in the array + for (%i = 0; %i < %count; %i++) + { + if ($TeamRank[%team, %i] == %client) + return; + } + + //add the client in at the bottom of the list, and increment the count + $TeamRank[%team, %count] = %client; + $TeamRank[%team, count] = $TeamRank[%team, count] + 1; + + //now recalculate the team rank for this player + %game.recalcTeamRanks(%client); +} + +function DefaultGame::removeFromTeamRankArray(%game, %client) +{ + //note, this should be called *before* the client actually switches teams or drops... + if (%client <= 0 || %client.team <= 0) + return; + + //find the correct team + if (%game.numTeams == 1) + %team = 0; + else + %team = %client.team; + + //now search throught the team rank array, looking for this client + %count = $TeamRank[%team, count]; + for (%i = 0; %i < %count; %i++) + { + if ($TeamRank[%team, %i] == %client) + { + //we've found the client in the array, now loop through, and move everyone else up a rank + for (%j = %i + 1; %j < %count; %j++) + { + %cl = $TeamRank[%team, %j]; + $TeamRank[%team, %j - 1] = %cl; + messageClient(%cl, 'MsgYourRankIs', "", %j); + } + $TeamRank[%team, %count - 1] = ""; + + //now decrement the team rank array count, and break + $TeamRank[%team, count] = $TeamRank[%team, count] - 1; + break; + } + } +} + +function DefaultGame::recalcTeamRanks(%game, %client) +{ + if (%client <= 0 || %client.team <= 0) + return; + + // this is a little confusing -- someone's actual numerical rank is always + // one number higher than his index in the $TeamRank array + // (e.g. person ranked 1st has index of 0) + + // TINMAN: I'm going to remove the %client.teamRank field - the index in the + // $TeamRank array already contains their rank - safer to search the array than + // to maintiain the information in a separate variable... + + //find the team, the client in the team array + if (%game.numTeams == 1) + %team = 0; + else + %team = %client.team; + + %count = $TeamRank[%team, count]; + %index = -1; + for (%i = 0; %i < %count; %i++) + { + if ($TeamRank[%team, %i] == %client) + { + %index = %i; + break; + } + } + + //if they weren't found in the array, return + if (%index < 0) + return; + + //make sure far down the array as they should be... + %tempIndex = %index; + %swapped = false; + while (true) + { + if (%tempIndex <= 0) + break; + + %tempIndex--; + %tempClient = $TeamRank[%team, %tempIndex]; + + //see if we should swap the two + if (%client.score > %tempClient.score) + { + %swapped = true; + %index = %tempIndex; + $TeamRank[%team, %tempIndex] = %client; + $TeamRank[%team, %tempIndex + 1] = %tempClient; + messageClient(%tempClient, 'MsgYourRankIs', "", %tempIndex + 2); + } + } + + //if we've swapped up at least once, we obviously won't need to swap down as well... + if (%swapped) + { + messageClient(%client, 'MsgYourRankIs', "", %index + 1); + return; + } + + //since we didnt' swap up, see if we need to swap down... + %tempIndex = %index; + %swapped = false; + while (true) + { + if (%tempIndex >= %count - 1) + break; + + %tempIndex++; + %tempClient = $TeamRank[%team, %tempIndex]; + + //see if we should swap the two + if (%client.score < %tempClient.score) + { + %swapped = true; + %index = %tempIndex; + $TeamRank[%team, %tempIndex] = %client; + $TeamRank[%team, %tempIndex - 1] = %tempClient; + messageClient(%tempClient, 'MsgYourRankIs', "", %tempIndex); + } + } + + //send the message (regardless of whether a swap happened or not) + messageClient(%client, 'MsgYourRankIs', "", %index + 1); +} + +function DefaultGame::recalcScore(%game, %cl) +{ + %game.recalcTeamRanks(%cl); +} + +function DefaultGame::testKill(%game, %victimID, %killerID) +{ + return ((%killerID !=0) && (%victimID.team != %killerID.team)); +} + +function DefaultGame::testSuicide(%game, %victimID, %killerID, %damageType) +{ + return ((%victimID == %killerID) || (%damageType == $DamageType::Ground) || (%damageType == $DamageType::Suicide)); +} + +function DefaultGame::testTeamKill(%game, %victimID, %killerID) +{ + return (%killerID.team == %victimID.team); +} + +function DefaultGame::testTurretKill(%game, %implement) +{ + if(%implement == 0) + return false; + else + return (%implement.getClassName() $= "Turret"); +} + +// function DefaultGame::awardScoreFlagCap(%game, %cl) +// { +// %cl.flagCaps++; +// $TeamScore[%cl.team] += %game.SCORE_PER_TEAM_FLAG_CAP; +// messageAll('MsgCTFTeamScore', "", %cl.team, $TeamScore[%cl.team]); +// +// if (%game.SCORE_PER_PLYR_FLAG_CAP > 1) +// %plural = "s"; +// else +// %plural = ""; +// +// if (%game.SCORE_PER_PLYR_FLAG_CAP != 0) +// messageClient(%cl, 'scoreFlaCapMsg', 'You received %1 point%2 for capturing the flag.', %game.SCORE_PER_PLYR_FLAG_CAP, %plural); +// %game.recalcScore(%cl); +// } + + +function DefaultGame::testOOBDeath(%game, %damageType) +{ + return (%damageType == $DamageType::OutOfBounds); +} + +function DefaultGame::awardScoreTurretKill(%game, %victimID, %implement) +{ + if ((%killer = %implement.getControllingClient()) != 0) //award whoever might be controlling the turret + { + if (%killer == %victimID) + %game.awardScoreSuicide(%victimID); + else if (%killer.team == %victimID.team) //player controlling a turret killed a teammate + { + %killer.teamKills++; + %game.awardScoreTurretTeamKill(%victimID, %killer); + %game.awardScoreDeath(%victimID); + } + else + { + %killer.turretKills++; + %game.recalcScore(%killer); + %game.awardScoreDeath(%victimID); + } + } + else if ((%killer = %implement.owner) != 0) //if it isn't controlled, award score to whoever deployed it + { + if (%killer.team == %victimID.team) + { + %game.awardScoreDeath(%victimID); + } + else + { + %killer.turretKills++; + %game.recalcScore(%killer); + %game.awardScoreDeath(%victimID); + } + } + //default is, no one was controlling it, no one owned it. No score given. +} + +function DefaultGame::awardScoreDeath(%game, %victimID) +{ + %victimID.deaths++; + if ( %game.SCORE_PER_DEATH != 0 ) + { +// %plural = (abs(%game.SCORE_PER_DEATH) != 1 ? "s" : ""); +// messageClient(%victimID, 'MsgScoreDeath', '\c0You have been penalized %1 point%2 for dying.', abs(%game.SCORE_PER_DEATH), %plural); + %game.recalcScore(%victimID); + } +} + +function DefaultGame::awardScoreKill(%game, %killerID) +{ + %killerID.kills++; + %game.recalcScore(%killerID); +} + +function DefaultGame::awardScoreSuicide(%game, %victimID) +{ + %victimID.suicides++; +// if (%game.SCORE_PER_SUICIDE != 0) +// messageClient(%victimID, 'MsgScoreSuicide', '\c0You have been penalized for killing yourself.'); + %game.recalcScore(%victimID); +} + +function DefaultGame::awardScoreTeamkill(%game, %victimID, %killerID) +{ + %killerID.teamKills++; + if (%game.SCORE_PER_TEAMKILL != 0) + messageClient(%killerID, 'MsgScoreTeamkill', '\c0You have been penalized for killing teammate %1.', %victimID.name); + + %game.recalcScore(%killerID); + // z0dd - ZOD, 8/9/03. Auto vote TKers + if(!$Host::TournamentMode) + { + if(($Host::ClassicTkLimit > 4 && %killerID.teamKills >= $Host::ClassicTkLimit) && (getAdmin() == 0)) + { + serverCmdStartNewVote(%victimID, "VoteKickPlayer", %killerID, 0, 0, 0, true); + bottomPrintAll("" @ %killerID.nameBase @ " Has " @ %killerID.teamKills @ " team kills. Recommend voting yes.", 4, 2); + logEcho(%killerID.nameBase @ " GUID: " @ %killerID.guid @ " TKS: " @ %killerID.teamKills, 1); + } + //else + //{ + // BottomPrint(%killerID, "You have " @ %killerID.teamKills @ ", you better cut it out!", 2, 1 ); + //} + } +} + +function DefaultGame::awardScoreTurretTeamKill(%game, %victimID, %killerID) +{ + %killerID.teamKills++; + if (%game.SCORE_PER_TEAMKILL != 0) + messageClient(%killerID, 'MsgScoreTeamkill', '\c0You have been penalized for killing your teammate %1, with a turret.', %victimID.name); + + %game.recalcScore(%killerID); + // z0dd - ZOD, 6/12/03. Auto vote TKers + if(!$Host::TournamentMode) + { + if(($Host::ClassicTkLimit > 4 && %killerID.teamKills >= $Host::ClassicTkLimit) && (getAdmin() == 0)) + { + serverCmdStartNewVote(%victimID, "VoteKickPlayer", %killerID, 0, 0, 0, true); + bottomPrintAll("" @ %killerID.nameBase @ " Has " @ %killerID.teamKills @ " team kills. Recommend voting yes.", 4, 2); + logEcho(%killerID.nameBase @ " GUID: " @ %killerID.guid @ " TKS: " @ %killerID.teamKills, 1); + } + else + { + BottomPrint(%killerID, "You have " @ %killerID.teamKills @ ", you better cut it out!", 2, 1 ); + } + } +} + + +function DefaultGame::objectRepaired(%game, %obj, %objName) +{ + %item = %obj.getDataBlock().getName(); + //echo("Item repaired is a " @ %item); + switch$ (%item) + { + case generatorLarge : + %game.genOnRepaired(%obj, %objName); + case stationInventory : + %game.stationOnRepaired(%obj, %objName); + case sensorMediumPulse : + %game.sensorOnRepaired(%obj, %objName); + case sensorLargePulse : + %game.sensorOnRepaired(%obj, %objName); + case turretBaseLarge : + %game.turretOnRepaired(%obj, %objName); + case stationVehicle : %game.vStationOnRepaired(%obj, %objName); + default: //unused by current gametypes. Add more checks here if desired + } +} + +function DefaultGame::allowsProtectedStatics(%game) +{ + return false; +} + +// jff: why is game object doing this? +//Return a simple string with no extras +function DefaultGame::cleanWord(%game, %this) +{ + %length = strlen(%this); + for(%i = 0; %i < %length; %i++) + { + %char = getSubStr(%this, %i, 1); + if(%char $= "_") + { + %next = getSubStr(%this, (%i+1), 1); + if(%next $= "_") + { + %char = "'"; //apostrophe (2 chars) + %i++; + } + else + %char = " "; //space + } + %clean = (%clean @ %char); + } +} + +function DefaultGame::stationOnEnterTrigger(%game, %data, %obj, %colObj) +{ + return true; +} + +function DefaultGame::WeaponOnUse(%game, %data, %obj) +{ + return true; +} + +function DefaultGame::HandInvOnUse(%game, %data, %obj) +{ + return true; +} + +function DefaultGame::WeaponOnInventory(%game, %this, %obj, %amount) +{ + return true; +} + +function DefaultGame::ObserverOnTrigger(%game, %data, %obj, %trigger, %state) +{ + return true; +} + +// jff: why is the game being notified that a weapon is being thrown? hot potato gametype? +function DefaultGame::ShapeThrowWeapon(%game, %this) +{ + return true; +} + +function DefaultGame::leaveMissionArea(%game, %playerData, %player) +{ + if(%player.getState() $= "Dead") + return; + + %player.client.outOfBounds = true; + messageClient(%player.client, 'LeaveMissionArea', '\c1You left the mission area.~wfx/misc/warning_beep.wav'); +} + +function DefaultGame::enterMissionArea(%game, %playerData, %player) +{ + if(%player.getState() $= "Dead") + return; + + %player.client.outOfBounds = false; + messageClient(%player.client, 'EnterMissionArea', '\c1You are back in the mission area.'); +} + +//------------------------------------------------------------------------------ +// AI stubs: +//------------------------------------------------------------------------------ + +function DefaultGame::onAIDamaged(%game, %clVictim, %clAttacker, %damageType, %sourceObject) +{ +} + +function DefaultGame::onAIFriendlyFire(%game, %clVictim, %clAttacker, %damageType, %sourceObject) +{ +} + +function DefaultGame::onAIKilled(%game, %clVictim, %clKiller, %damageType, %implement) +{ + //unassign the client from any objectives + AIUnassignClient(%clVictim); + + //break the link, if this ai is controlled + aiReleaseHumanControl(%clVictim.controlByHuman, %clVictim); + + //and schedule the respawn + %clVictim.respawnThread = schedule(5000, %clVictim, "onAIRespawn", %clVictim); +} + +function DefaultGame::onAIKilledClient(%game, %clVictim, %clAttacker, %damageType, %implement) +{ + %clAttacker.setVictim(%clVictim, %clVictim.player); +} + +//------------------------------------------------------------------------------ +// Voting stuff: +//------------------------------------------------------------------------------ +function DefaultGame::sendGamePlayerPopupMenu( %game, %client, %targetClient, %key ) +{ + if( !%targetClient.matchStartReady ) + return; + + %isAdmin = ( %client.isAdmin || %client.isSuperAdmin ); + %isTargetSelf = ( %client == %targetClient ); + %isTargetAdmin = ( %targetClient.isAdmin || %targetClient.isSuperAdmin ); + %isTargetBot = %targetClient.isAIControlled(); + %isTargetObserver = ( %targetClient.team == 0 ); + %outrankTarget = false; + + if ( %client.isSuperAdmin ) // z0dd - ZOD, 7/11/03. Super admins should outrank even themseleves. + %outrankTarget = 1; //!%targetClient.isSuperAdmin; + else if ( %client.isAdmin ) + %outrankTarget = !%targetClient.isAdmin; + + if( %client.isSuperAdmin && %targetClient.guid != 0 ) // z0dd - ZOD, 9/29/02. Removed T2 demo code from here + { + messageClient( %client, 'MsgPlayerPopupItem', "", %key, "addAdmin", "", 'Add to Server Admin List', 10); + messageClient( %client, 'MsgPlayerPopupItem', "", %key, "addSuperAdmin", "", 'Add to Server SuperAdmin List', 11); + } + + //mute options + if ( !%isTargetSelf ) + { + if ( %client.muted[%targetClient] ) + messageClient( %client, 'MsgPlayerPopupItem', "", %key, "MutePlayer", "", 'Unmute Text Chat', 1); + else + messageClient( %client, 'MsgPlayerPopupItem', "", %key, "MutePlayer", "", 'Mute Text Chat', 1); + + if ( !%isTargetBot && %client.canListenTo( %targetClient ) ) + { + if ( %client.getListenState( %targetClient ) ) + messageClient( %client, 'MsgPlayerPopupItem', "", %key, "ListenPlayer", "", 'Disable Voice Com', 9 ); + else + messageClient( %client, 'MsgPlayerPopupItem', "", %key, "ListenPlayer", "", 'Enable Voice Com', 9 ); + } + // ------------------------------------------ + // z0dd - ZOD 4/4/02. Observe a specific player + if (%client.team == 0 && !%isTargetObserver) + messageClient(%client, 'MsgPlayerPopupItem', "", %key, "ObservePlayer", "", 'Observe Player', 12); + } + if( !%client.canVote && !%isAdmin ) + return; + + // regular vote options on players + if ( %game.scheduleVote $= "" && !%isAdmin && !%isTargetAdmin ) + { + if ( $Host::allowAdminPlayerVotes && !%isTargetBot ) // z0dd - ZOD, 9/29/02. Removed T2 demo code from here + messageClient( %client, 'MsgPlayerPopupItem', "", %key, "AdminPlayer", "", 'Vote to Make Admin', 2 ); + + if ( !%isTargetSelf ) + { + messageClient( %client, 'MsgPlayerPopupItem', "", %key, "KickPlayer", "", 'Vote to Kick', 3 ); + } + } + // Admin only options on players: + else if ( %isAdmin ) // z0dd - ZOD, 9/29/02. Removed T2 demo code from here + { + if ( !%isTargetBot && !%isTargetAdmin ) + messageClient( %client, 'MsgPlayerPopupItem', "", %key, "AdminPlayer", "", 'Make Admin', 2 ); + + if ( !%isTargetSelf && %outrankTarget ) + { + messageClient( %client, 'MsgPlayerPopupItem', "", %key, "KickPlayer", "", 'Kick', 3 ); + + if ( !%isTargetBot ) + { + // ------------------------------------------------------------------------------------------------------ + // z0dd - ZOD - Founder 7/13/03. Bunch of new admin features + messageClient(%client, 'MsgPlayerPopupItem', "", %key, "Warn", "", 'Warn player', 13); + if(%isTargetAdmin) + messageClient( %client, 'MsgPlayerPopupItem', "", %key, "StripAdmin", "", 'Strip admin', 14 ); + + messageClient( %client, 'MsgPlayerPopupItem', "", %key, "SendMessage", "", 'Send Private Message', 15 ); + messageClient( %client, 'MsgPlayerPopupItem', "", %key, "PrintClientInfo", "", 'Client Info', 16 ); // z0dd - ZOD - MeBad, 7/13/03. Send client information. + + if( %client.isSuperAdmin ) + { + messageClient( %client, 'MsgPlayerPopupItem', "", %key, "BanPlayer", "", 'Ban', 4 ); + + if ( %targetClient.isGagged ) + messageClient( %client, 'MsgPlayerPopupItem', "", %key, "UnGagPlayer", "", 'UnGag Player', 17); + else + messageClient( %client, 'MsgPlayerPopupItem', "", %key, "GagPlayer", "", 'Gag Player', 17); + + if ( %targetClient.isFroze ) + messageClient( %client, 'MsgPlayerPopupItem', "", %key, "ThawPlayer", "", 'Thaw Player', 18); + else + messageClient( %client, 'MsgPlayerPopupItem', "", %key, "FreezePlayer", "", 'Freeze Player', 18); + + messageClient( %client, 'MsgPlayerPopupItem', "", %key, "BootPlayer", "", 'Boot to the Rear', 19); + messageClient( %client, 'MsgPlayerPopupItem', "", %key, "ExplodePlayer", "", 'Explode Player', 20); + } + if ( !%isTargetObserver ) + { + messageClient( %client, 'MsgPlayerPopupItem', "", %key, "ToObserver", "", 'Force observer', 5 ); + } + } + } + if ( %isTargetSelf || %outrankTarget ) + { + if(%isTargetAdmin) + messageClient( %client, 'MsgPlayerPopupItem', "", %key, "StripAdmin", "", 'Strip admin', 14 ); + + if ( %game.numTeams > 1 ) + { + if ( %isTargetObserver ) + { + %action = %isTargetSelf ? "Join " : "Change to "; + %str1 = %action @ getTaggedString( %game.getTeamName(1) ); + %str2 = %action @ getTaggedString( %game.getTeamName(2) ); + + messageClient( %client, 'MsgPlayerPopupItem', "", %key, "ChangeTeam", "", %str1, 6 ); + messageClient( %client, 'MsgPlayerPopupItem', "", %key, "ChangeTeam", "", %str2, 7 ); + } + else + { + %changeTo = %targetClient.team == 1 ? 2 : 1; + %str = "Switch to " @ getTaggedString( %game.getTeamName(%changeTo) ); + %caseId = 5 + %changeTo; + + messageClient( %client, 'MsgPlayerPopupItem', "", %key, "ChangeTeam", "", %str, %caseId ); + + // z0dd - ZOD, 7/11/03. Allow Super admins to force themselves to obs. + messageClient( %client, 'MsgPlayerPopupItem', "", %key, "ToObserver", "", 'Force observer', 5 ); + } + } + else if ( %isTargetObserver ) + { + %str = %isTargetSelf ? 'Join the Game' : 'Add to Game'; + messageClient( %client, 'MsgPlayerPopupItem', "", %key, "JoinGame", "", %str, 8 ); + } + } + } +} + +//------------------------------------------------------------------------------ +function DefaultGame::sendGameVoteMenu( %game, %client, %key ) +{ + %isAdmin = ( %client.isAdmin || %client.isSuperAdmin ); + %multipleTeams = %game.numTeams > 1; + + // no one is going anywhere until this thing starts + if($MatchStarted) + { + // Client options: + if ( %client.team != 0 ) + { + if ( %multipleTeams ) + if( !$Host::TournamentMode ) + messageClient( %client, 'MsgVoteItem', "", %key, 'ChooseTeam', "", 'Change your Team' ); + messageClient( %client, 'MsgVoteItem', "", %key, 'MakeObserver', "", 'Become an Observer' ); + } + else + { + if(!%multipleTeams && !$Host::TournamentMode) + messageClient( %client, 'MsgVoteItem', "", %key, 'JoinGame', "", 'Join the Game' ); + } + + //%totalSlots = $Host::maxPlayers - ($HostGamePlayerCount + $HostGameBotCount); + // if( $HostGameBotCount > 0 && %totalSlots > 0 && %client.isAdmin) + //messageClient( %client, 'MsgVoteItem', "", %key, 'Addbot', "", 'Add a Bot' ); + } + + if( !%client.canVote && !%isAdmin ) + return; + + // z0dd - ZOD, 9/29/02. Removed T2 demo code from here + + if ( %game.scheduleVote $= "" ) + { + if(!%client.isAdmin) + { + // Actual vote options: + messageClient( %client, 'MsgVoteItem', "", %key, 'VoteChangeMission', 'change the mission to', 'Vote to Change the Mission' ); + messageClient( %client, 'MsgVoteItem', "", %key, 'VoteSkipMission', 'skip the mission to', 'Vote to Skip Mission' ); + + if( $Host::TournamentMode ) + { + messageClient( %client, 'MsgVoteItem', "", %key, 'VoteFFAMode', 'Change server to Free For All.', 'Vote Free For All Mode' ); + + if(!$MatchStarted && !$CountdownStarted) + messageClient( %client, 'MsgVoteItem', "", %key, 'VoteMatchStart', 'Start Match', 'Vote to Start the Match' ); + } + else + messageClient( %client, 'MsgVoteItem', "", %key, 'VoteTournamentMode', 'Change server to Tournament.', 'Vote Tournament Mode' ); + + if ( %multipleTeams ) + { + if(!$MatchStarted && !$Host::TournamentMode) + messageClient( %client, 'MsgVoteItem', "", %key, 'ChooseTeam', "", 'Change your Team' ); + + if ( $teamDamage ) + messageClient( %client, 'MsgVoteItem', "", %key, 'VoteTeamDamage', 'disable team damage', 'Vote to Disable Team Damage' ); + else + messageClient( %client, 'MsgVoteItem', "", %key, 'VoteTeamDamage', 'enable team damage', 'Vote to Enable Team Damage' ); + + if($CurrentMissionType !$= TR2) // z0dd - ZOD, 5/23/03. Added vote for Random and Fair teams + { + if ( $RandomTeams ) + messageClient( %client, 'MsgVoteItem', "", %key, 'VoteRandomTeams', 'disable random teams', 'Vote to Disable Random Teams' ); + else + messageClient( %client, 'MsgVoteItem', "", %key, 'VoteRandomTeams', 'enable random teams', 'Vote to Enable Random Teams' ); + + if ( $FairTeams ) + messageClient( %client, 'MsgVoteItem', "", %key, 'VoteFairTeams', 'disable fair teams', 'Vote to Disable Fair Teams' ); + else + messageClient( %client, 'MsgVoteItem', "", %key, 'VoteFairTeams', 'enable fair teams', 'Vote to Enable Fair Teams' ); + } + } + } + else + { + // Actual vote options: + messageClient( %client, 'MsgVoteItem', "", %key, 'VoteChangeMission', 'change the mission to', 'Change the Mission' ); + messageClient( %client, 'MsgVoteItem', "", %key, 'VoteSkipMission', 'skip the mission to', 'Skip the Mission' ); + + if( $Host::TournamentMode ) + { + messageClient( %client, 'MsgVoteItem', "", %key, 'VoteFFAMode', 'Change server to Free For All.', 'Free For All Mode' ); + + if(!$MatchStarted && !$CountdownStarted) + messageClient( %client, 'MsgVoteItem', "", %key, 'VoteMatchStart', 'Start Match', 'Start Match' ); + } + else + messageClient( %client, 'MsgVoteItem', "", %key, 'VoteTournamentMode', 'Change server to Tournament.', 'Tournament Mode' ); + + if ( %multipleTeams ) + { + if(!$MatchStarted) + messageClient( %client, 'MsgVoteItem', "", %key, 'ChooseTeam', "", 'Choose Team' ); + + if ( $teamDamage ) + messageClient( %client, 'MsgVoteItem', "", %key, 'VoteTeamDamage', 'disable team damage', 'Disable Team Damage' ); + else + messageClient( %client, 'MsgVoteItem', "", %key, 'VoteTeamDamage', 'enable team damage', 'Enable Team Damage' ); + + if($CurrentMissionType !$= TR2) // z0dd - ZOD, 5/23/03. Added vote for Random and Fair teams + { + if ( $RandomTeams ) + messageClient( %client, 'MsgVoteItem', "", %key, 'VoteRandomTeams', 'disable random teams', 'Disable Random Teams' ); + else + messageClient( %client, 'MsgVoteItem', "", %key, 'VoteRandomTeams', 'enable random teams', 'Enable Random Teams' ); + + if ( $FairTeams ) + messageClient( %client, 'MsgVoteItem', "", %key, 'VoteFairTeams', 'disable fair teams', 'Disable Fair Teams' ); + else + messageClient( %client, 'MsgVoteItem', "", %key, 'VoteFairTeams', 'enable fair teams', 'Enable Fair Teams' ); + } + } + } + } + + // Admin only options: + if ( %client.isAdmin ) + { + if ( $LimitArmors ) + messageClient( %client, 'MsgVoteItem', "", %key, 'VoteArmorLimits', 'disable armor limiting', 'Disable armor limits' ); + else + messageClient( %client, 'MsgVoteItem', "", %key, 'VoteArmorLimits', 'enable armor limiting', 'Enable armor limits' ); + + messageClient( %client, 'MsgVoteItem', "", %key, 'VoteChangeTimeLimit', 'change the time limit', 'Change the Time Limit' ); + messageClient( %client, 'MsgVoteItem', "", %key, 'VoteResetServer', 'reset server defaults', 'Reset the Server' ); + messageClient( %client, 'MsgVoteItem', "", %key, 'VoteClearServer', 'clear server for match', 'Clear Server for Tournament' ); + + // ----------------------------------------------------------------------------- + // z0dd - ZOD, 5/12/02. Add bot menu for admins + %totalSlots = $Host::maxPlayers - ($HostGamePlayerCount + $HostGameBotCount); + if( $HostGameBotCount > 0 && %totalSlots > 0) + messageClient( %client, 'MsgVoteItem', "", %key, 'Addbot', "", 'Add a Bot' ); + // ----------------------------------------------------------------------------- + } +} + +//------------------------------------------------------------------------------ +function DefaultGame::sendGameTeamList( %game, %client, %key ) +{ + %teamCount = %game.numTeams; + if ( %teamCount < 2 ) + { + warn( "Team menu requested for one-team game!" ); + return; + } + + for ( %team = 1; %team - 1 < %teamCount; %team++ ) + messageClient( %client, 'MsgVoteItem', "", %key, %team, "", detag( getTaggedString( %game.getTeamName(%team) ) ) ); +} + +//------------------------------------------------------------------------------ +function DefaultGame::sendTimeLimitList( %game, %client, %key ) +{ + messageClient( %client, 'MsgVoteItem', "", %key, 90, "", '90 minutes' ); + messageClient( %client, 'MsgVoteItem', "", %key, 120, "", '120 minutes' ); + messageClient( %client, 'MsgVoteItem', "", %key, 150, "", '150 minutes' ); + messageClient( %client, 'MsgVoteItem', "", %key, 180, "", '180 minutes' ); + messageClient( %client, 'MsgVoteItem', "", %key, 240, "", '240 minutes' ); + messageClient( %client, 'MsgVoteItem', "", %key, 360, "", '360 minutes' ); + messageClient( %client, 'MsgVoteItem', "", %key, 480, "", '480 minutes' ); + messageClient( %client, 'MsgVoteItem', "", %key, 999, "", 'No time limit' ); +} + +//------------------------------------------------------------------------------ +// all global votes here +// this function was created to remove the call to "eval", which is non-functional in PURE servers... +function DefaultGame::evalVote(%game, %typeName, %admin, %arg1, %arg2, %arg3, %arg4) +{ + switch$ (%typeName) + { + case "voteChangeMission": + %game.voteChangeMission(%admin, %arg1, %arg2, %arg3, %arg4); + + case "voteTeamDamage": + %game.voteTeamDamage(%admin, %arg1, %arg2, %arg3, %arg4); + + case "voteTournamentMode": + %game.voteTournamentMode(%admin, %arg1, %arg2, %arg3, %arg4); + + case "voteMatchStart": + %game.voteMatchStart(%admin, %arg1, %arg2, %arg3, %arg4); + + case "voteFFAMode": + %game.voteFFAMode(%admin, %arg1, %arg2, %arg3, %arg4); + + case "voteChangeTimeLimit": + %game.voteChangeTimeLimit(%admin, %arg1, %arg2, %arg3, %arg4); + + case "voteResetServer": + %game.voteResetServer(%admin, %arg1, %arg2, %arg3, %arg4); + + case "voteKickPlayer": + %game.voteKickPlayer(%admin, %arg1, %arg2, %arg3, %arg4); + + case "voteAdminPlayer": + %game.voteAdminPlayer(%admin, %arg1, %arg2, %arg3, %arg4); + + case "voteGreedMode": + %game.voteGreedMode(%admin, %arg1, %arg2, %arg3, %arg4); + + case "voteHoardMode": + %game.voteHoardMode(%admin, %arg1, %arg2, %arg3, %arg4); + + // z0dd - ZOD, 5/23/03. Added vote for Random, Fair teams and armor limiting + case "voteRandomTeams": + %game.voteRandomTeams(%admin, %arg1, %arg2, %arg3, %arg4); + + case "voteFairTeams": + %game.voteFairTeams(%admin, %arg1, %arg2, %arg3, %arg4); + + case "voteArmorLimits": + %game.voteArmorLimits(%admin, %arg1, %arg2, %arg3, %arg4); + + case "voteClearServer": + %game.voteClearServer(%admin, %arg1, %arg2, %arg3, %arg4); + + case "voteSkipMission": + %game.voteSkipMission(%admin, %arg1, %arg2, %arg3, %arg4); + } +} + +function DefaultGame::voteChangeMission(%game, %admin, %missionDisplayName, %typeDisplayName, %missionId, %missionTypeId) +{ + %mission = $HostMissionFile[%missionId]; + if ( %mission $= "" ) + { + error( "Invalid mission index passed to DefaultGame::voteChangeMission!" ); + return; + } + + %missionType = $HostTypeName[%missionTypeId]; + if ( %missionType $= "" ) + { + error( "Invalid mission type id passed to DefaultGame::voteChangeMission!" ); + return; + } + + if(%admin) + { + messageAll('MsgAdminChangeMission', '\c2The Admin %3 has changed the mission to %1 (%2).', %missionDisplayName, %typeDisplayName, $AdminCl.name ); + logEcho($AdminCl.nameBase @ ": mission changed to "@%missionDisplayName@"/"@%typeDisplayName@" (admin)"); + %game.gameOver(); + loadMission( %mission, %missionType, false ); + } + else + { + %totalVotes = %game.totalVotesFor + %game.totalVotesAgainst; + if(%totalVotes > 0 && (%game.totalVotesFor / (ClientGroup.getCount() - $HostGameBotCount)) > ($Host::VotePasspercent / 100)) + { + messageAll('MsgVotePassed', '\c2The mission was changed to %1 (%2) by vote.', %missionDisplayName, %typeDisplayName ); + logEcho("mission changed to "@%missionDisplayName@"/"@%typeDisplayName@" (vote)"); + %game.gameOver(); + loadMission( %mission, %missionType, false ); + } + else + messageAll('MsgVoteFailed', '\c2Change mission vote did not pass: %1 percent.', mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount - %game.totalVotesNone) * 100)); + } +} + +//------------------------------------------------------------------------------ +function DefaultGame::voteTeamDamage(%game, %admin) +{ + %setto = ""; + %cause = ""; + if(%admin) + { + if($teamDamage) + { + messageAll('MsgAdminForce', '\c2The Admin %1 has disabled team damage.', $AdminCl.name); + $Host::TeamDamageOn = $TeamDamage = 0; + %setto = "disabled"; + } + else + { + messageAll('MsgAdminForce', '\c2The Admin %1 has enabled team damage.', $AdminCl.name); + $Host::TeamDamageOn = $TeamDamage = 1; + %setto = "enabled"; + } + %cause = "(admin)"; + } + else + { + %totalVotes = %game.totalVotesFor + %game.totalVotesAgainst; + // Added people who dont vote into the equation, now if you do not vote, it doesn't count as a no. - z0dd - ZOD + if(%totalVotes > 0 && (%game.totalVotesFor / (ClientGroup.getCount() - $HostGameBotCount - %game.totalVotesNone)) > ($Host::VotePasspercent / 100)) + { + if($teamDamage) + { + messageAll('MsgVotePassed', '\c2Team damage was disabled by vote.'); + $Host::TeamDamageOn = $TeamDamage = 0; + %setto = "disabled"; + } + else + { + messageAll('MsgVotePassed', '\c2Team damage was enabled by vote.'); + $Host::TeamDamageOn = $TeamDamage = 1; + %setto = "enabled"; + } + %cause = "(vote)"; + } + else + { + if($teamDamage) + messageAll('MsgVoteFailed', '\c2Disable team damage vote did not pass: %1 percent.', mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount - %game.totalVotesNone) * 100)); + else + messageAll('MsgVoteFailed', '\c2Enable team damage vote did not pass: %1 percent.', mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount - %game.totalVotesNone) * 100)); + } + } + if(%setto !$= "") + logEcho($AdminCl.nameBase @ ": team damage "@%setto SPC %cause, 1); +} + +//------------------------------------------------------------------------------ +function DefaultGame::voteTournamentMode( %game, %admin, %missionDisplayName, %typeDisplayName, %missionId, %missionTypeId ) +{ + %mission = $HostMissionFile[%missionId]; + if ( %mission $= "" ) + { + error( "Invalid mission index passed to DefaultGame::voteTournamentMode!" ); + return; + } + + %missionType = $HostTypeName[%missionTypeId]; + if ( %missionType $= "" ) + { + error( "Invalid mission type id passed to DefaultGame::voteTournamentMode!" ); + return; + } + + %cause = ""; + if (%admin) + { + messageAll( 'MsgAdminForce', '\c2The Admin %2 has switched the server to Tournament mode (%1).', %missionDisplayName, $AdminCl.name ); + setModeTournament( %mission, %missionType ); + %cause = "(admin)"; + } + else + { + %totalVotes = %game.totalVotesFor + %game.totalVotesAgainst; + // Added people who dont vote into the equation, now if you do not vote, it doesn't count as a no. - z0dd - ZOD + if(%totalVotes > 0 && (%game.totalVotesFor / (ClientGroup.getCount() - $HostGameBotCount - %game.totalVotesNone)) > ($Host::VotePasspercent / 100)) + { + messageAll('MsgVotePassed', '\c2Server switched to Tournament mode by vote (%1): %2 percent.', %missionDisplayName, mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount - %game.totalVotesNone) * 100)); + setModeTournament( %mission, %missionType ); + %cause = "(vote)"; + } + else + messageAll('MsgVoteFailed', '\c2Tournament mode vote did not pass: %1 percent.', mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount - %game.totalVotesNone) * 100)); + } + if(%cause !$= "") + logEcho($AdminCl.nameBase @ ": tournament mode set "@%cause, 1); +} + +//------------------------------------------------------------------------------ +function DefaultGame::voteMatchStart( %game, %admin) +{ + %cause = ""; + %ready = forceTourneyMatchStart(); + if(%admin) + { + if(!%ready) + { + // z0dd - ZOD, 5/19/03. This was sending to %client, there is no %client declared, duh + messageClient( $AdminCl, 'msgClient', '\c2No players are ready yet.'); + return; + } + else + { + messageAll('msgMissionStart', '\c2The admin %1 has forced the match to start.', $AdminCl.name); + %cause = "(admin)"; + startTourneyCountdown(); + } + } + else + { + if(!%ready) + { + messageAll( 'msgClient', '\c2Vote passed to start match, but no players are ready yet.'); + return; + } + else + { + %totalVotes = %game.totalVotesFor + %game.totalVotesAgainst; + // Added people who dont vote into the equation, now if you do not vote, it doesn't count as a no. - z0dd - ZOD + if(%totalVotes > 0 && (%game.totalVotesFor / (ClientGroup.getCount() - $HostGameBotCount - %game.totalVotesNone)) > ($Host::VotePasspercent / 100)) + { + messageAll('MsgVotePassed', '\c2The match has been started by vote: %1 percent.', mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount - %game.totalVotesNone) * 100)); + startTourneyCountdown(); + } + else + messageAll('MsgVoteFailed', '\c2Start Match vote did not pass: %1 percent.', mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount - %game.totalVotesNone) * 100)); + } + } + + if(%cause !$= "") + logEcho($AdminCl.nameBase @ ": start match "@%cause, 1); +} + +//------------------------------------------------------------------------------ +function DefaultGame::voteFFAMode( %game, %admin, %client ) +{ + %cause = ""; + %name = getTaggedString(%client.name); + + if (%admin) + { + messageAll('MsgAdminForce', '\c2The Admin %1 has switched the server to Free For All mode.', $AdminCl.name); + setModeFFA($CurrentMission, $CurrentMissionType); + %cause = "(admin)"; + } + else + { + %totalVotes = %game.totalVotesFor + %game.totalVotesAgainst; + // Added people who dont vote into the equation, now if you do not vote, it doesn't count as a no. - z0dd - ZOD + if(%totalVotes > 0 && (%game.totalVotesFor / (ClientGroup.getCount() - $HostGameBotCount - %game.totalVotesNone)) > ($Host::VotePasspercent / 100)) + { + messageAll('MsgVotePassed', '\c2Server switched to Free For All mode by vote.', %client); + setModeFFA($CurrentMission, $CurrentMissionType); + %cause = "(vote)"; + } + else + messageAll('MsgVoteFailed', '\c2Free For All mode vote did not pass: %1 percent.', mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount - %game.totalVotesNone) * 100)); + } + if(%cause !$= "") + logEcho($AdminCl.nameBase @ ": free for all set "@%cause, 1); +} + +//------------------------------------------------------------------------------ +function DefaultGame::voteChangeTimeLimit( %game, %admin, %newLimit ) +{ + if( %newLimit == 999 ) + %display = "unlimited"; + else + %display = %newLimit; + + %cause = ""; + if ( %admin ) + { + messageAll( 'MsgAdminForce', '\c2The Admin %2 changed the mission time limit to %1 minutes.', %display, $AdminCl.name ); + $Host::TimeLimit = %newLimit; + %cause = "(admin)"; + } + else + { + %totalVotes = %game.totalVotesFor + %game.totalVotesAgainst; + // Added people who dont vote into the equation, now if you do not vote, it doesn't count as a no. - z0dd - ZOD + if(%totalVotes > 0 && (%game.totalVotesFor / (ClientGroup.getCount() - $HostGameBotCount - %game.totalVotesNone)) > ($Host::VotePasspercent / 100)) + { + messageAll('MsgVotePassed', '\c2The mission time limit was set to %1 minutes by vote.', %display); + $Host::TimeLimit = %newLimit; + %cause = "(vote)"; + } + else + messageAll('MsgVoteFailed', '\c2The vote to change the mission time limit did not pass: %1 percent.', mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount - %game.totalVotesNone) * 100)); + } + + //if the time limit was actually changed... + if(%cause !$= "") + { + logEcho($AdminCl.nameBase @ ": time limit set to "@%display SPC %cause); + + //if the match has been started, reset the end of match countdown + if ($matchStarted) + { + //schedule the end of match countdown + %elapsedTimeMS = getSimTime() - $missionStartTime; + %curTimeLeftMS = ($Host::TimeLimit * 60 * 1000) - %elapsedTimeMS; + error("time limit="@$Host::TimeLimit@", elapsed="@(%elapsedTimeMS / 60000)@", curtimeleftms="@%curTimeLeftMS); + CancelEndCountdown(); + EndCountdown(%curTimeLeftMS); + cancel(%game.timeSync); + %game.checkTimeLimit(true); + } + } +} + +//------------------------------------------------------------------------------ +function DefaultGame::voteResetServer( %game, %admin, %client ) +{ + %cause = ""; + if ( %admin ) + { + messageAll( 'AdminResetServer', '\c2The Admin %1 has reset the server.', $AdminCl.name ); + resetServerDefaults(); + %cause = "(admin)"; + } + else + { + %totalVotes = %game.totalVotesFor + %game.totalVotesAgainst; + // Added people who dont vote into the equation, now if you do not vote, it doesn't count as a no. - z0dd - ZOD + if(%totalVotes > 0 && (%game.totalVotesFor / (ClientGroup.getCount() - $HostGameBotCount - %game.totalVotesNone)) > ($Host::VotePasspercent / 100)) + { + messageAll('MsgVotePassed', '\c2The Server has been reset by vote.' ); + resetServerDefaults(); + %cause = "(vote)"; + } + else + messageAll('MsgVoteFailed', '\c2The vote to reset Server to defaults did not pass: %1 percent.', mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount - %game.totalVotesNone) * 100)); + } + if(%cause !$= "") + logEcho($AdminCl.nameBase @ ": server reset "@%cause, 1); +} + +//------------------------------------------------------------------------------ +// all team based votes here +function DefaultGame::voteKickPlayer(%game, %admin, %client) +{ + %cause = ""; + + if(%admin) + { + kick(%client, %admin, %client.guid ); + %cause = "(admin)"; + } + else + { + %team = %client.team; + %totalVotes = %game.votesFor[%game.kickTeam] + %game.votesAgainst[%game.kickTeam]; + if(%totalVotes > 0 && (%game.votesFor[%game.kickTeam] / %totalVotes) > ($Host::VotePasspercent / 100)) + { + kick(%client, %admin, %game.kickGuid); + %cause = "(vote)"; + } + else + { + for ( %idx = 0; %idx < ClientGroup.getCount(); %idx++ ) + { + %cl = ClientGroup.getObject( %idx ); + + if (%cl.team == %game.kickTeam && !%cl.isAIControlled()) + messageClient( %cl, 'MsgVoteFailed', '\c2Kick player vote did not pass' ); + } + } + } + + %game.kickTeam = ""; + %game.kickGuid = ""; + %game.kickClientName = ""; + + if(%cause !$= "") + logEcho($AdminCl.nameBase @ ": " @ %name @ " (cl " @ %game.kickClient @ ") kicked " @ %cause, 1); +} + +//------------------------------------------------------------------------------ +function DefaultGame::banPlayer(%game, %admin, %client) +{ + %cause = ""; + %name = %client.nameBase; + if( %admin ) + { + ban( %client, %admin ); + %cause = "(admin)"; + } + + if(%cause !$= "") + logEcho($AdminCl.nameBase @ ": " @ %name@" (cl "@%client@") banned "@%cause, 1); +} + +//------------------------------------------------------------------------------ +function DefaultGame::voteAdminPlayer(%game, %admin, %client) +{ + %cause = ""; + + if (%admin) + { + messageAll('MsgAdminAdminPlayer', '\c2The Admin %3 made %2 an admin.', %client, %client.name, $AdminCl.name); + %client.isAdmin = 1; + %cause = "(admin)"; + } + else + { + %totalVotes = %game.totalVotesFor + %game.totalVotesAgainst; + // Added people who dont vote into the equation, now if you do not vote, it doesn't count as a no. - z0dd - ZOD + if(%totalVotes > 0 && (%game.totalVotesFor / (ClientGroup.getCount() - $HostGameBotCount - %game.totalVotesNone)) > ($Host::VotePasspercent / 100)) + { + messageAll('MsgAdminPlayer', '\c2%2 was made an admin by vote.', %client, %client.name); + %client.isAdmin = 1; + %cause = "(vote)"; + } + else + messageAll('MsgVoteFailed', '\c2Vote to make %1 an admin did not pass.', %client.name); + } + if(%cause !$= "") + logEcho($AdminCl.nameBase @ ": " @ %client.nameBase@" (cl "@%client@") made admin "@%cause, 1); +} + +//------------------------------------------------------------------------------ +function DefaultGame::processGameLink(%game, %client, %arg1, %arg2, %arg3, %arg4, %arg5) +{ + //the default behavior when clicking on a game link is to start observing that client + %targetClient = %arg1; + if ((%client.team == 0) && isObject(%targetClient) && (%targetClient.team != 0)) + { + %prevObsClient = %client.observeClient; + + // update the observer list for this client + observerFollowUpdate( %client, %targetClient, %prevObsClient !$= "" ); + + serverCmdObserveClient(%client, %targetClient); + displayObserverHud(%client, %targetClient); + + if (%targetClient != %prevObsClient) + { + messageClient(%targetClient, 'Observer', '\c1%1 is now observing you.', %client.name); + messageClient(%prevObsClient, 'ObserverEnd', '\c1%1 is no longer observing you.', %client.name); + } + } +} + +// z0dd - ZOD, 5/23/03. New function, vote for Random teams +function DefaultGame::voteRandomTeams(%game, %admin) +{ + %setto = ""; + %cause = ""; + if(%admin) + { + if($RandomTeams) + { + messageAll( 'MsgAdminForce', '\c3%1\c2: RANDOM TEAMS DISABLED. Changes will take place next mission.', $AdminCl.name); + $Host::ClassicRandomizeTeams = $RandomTeams = 0; + %setto = "disabled"; + } + else + { + messageAll( 'MsgAdminForce', '\c3%1\c2: RANDOM TEAMS ENABLED. Changes will take place next mission.', $AdminCl.name); + $Host::ClassicRandomizeTeams = $RandomTeams = 1; + %setto = "enabled"; + } + %cause = "(admin)"; + } + else + { + %totalVotes = %game.totalVotesFor + %game.totalVotesAgainst; + // Added people who dont vote into the equation, now if you do not vote, it doesn't count as a no. - z0dd - ZOD + if(%totalVotes > 0 && (%game.totalVotesFor / (ClientGroup.getCount() - $HostGameBotCount - %game.totalVotesNone)) > ($Host::VotePasspercent / 100)) + { + if($RandomTeams) + { + messageAll('MsgVotePassed', '\c3Vote\c2: RANDOM TEAMS DISABLED. Changes will take place next mission.'); + $Host::ClassicRandomizeTeams = $RandomTeams = 0; + %setto = "disabled"; + } + else + { + messageAll('MsgVotePassed', '\c3Vote\c2: RANDOM TEAMS ENABLED. Changes will take place next mission.'); + $Host::ClassicRandomizeTeams = $RandomTeams = 1; + %setto = "enabled"; + } + %cause = "(vote)"; + } + else + { + if($RandomTeams) + messageAll('MsgVoteFailed', '\c2Disable randomize teams vote did not pass: %1 percent.', mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount - %game.totalVotesNone) * 100)); + else + messageAll('MsgVoteFailed', '\c2Enable randomize teams vote did not pass: %1 percent.', mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount - %game.totalVotesNone) * 100)); + } + } + if(%setto !$= "") + logEcho($AdminCl.nameBase @ ": randomize teams "@%setto SPC %cause, 1); +} + +// z0dd - ZOD, 5/23/03. New function, vote for Fair teams +function DefaultGame::voteFairTeams(%game, %admin) +{ + %setto = ""; + %cause = ""; + if(%admin) + { + if($FairTeams) + { + messageAll( 'MsgAdminForce', '\c3%1\c2: FAIR TEAMS DISABLED. Changes will take place next mission.', $AdminCl.name); + $Host::ClassicFairTeams = $FairTeams = 0; + %setto = "disabled"; + } + else + { + messageAll( 'MsgAdminForce', '\c3%1\c2: FAIR TEAMS ENABLED. Changes will take place next mission.', $AdminCl.name); + $Host::ClassicFairTeams = $FairTeams = 1; + %setto = "enabled"; + } + %cause = "(admin)"; + } + else + { + %totalVotes = %game.totalVotesFor + %game.totalVotesAgainst; + // Added people who dont vote into the equation, now if you do not vote, it doesn't count as a no. - z0dd - ZOD + if(%totalVotes > 0 && (%game.totalVotesFor / (ClientGroup.getCount() - $HostGameBotCount - %game.totalVotesNone)) > ($Host::VotePasspercent / 100)) + { + if($FairTeams) + { + messageAll('MsgVotePassed', '\c3Vote\c2: FAIR TEAMS DISABLED. Changes will take place next mission.'); + $Host::ClassicFairTeams = $FairTeams = 0; + %setto = "disabled"; + } + else + { + messageAll('MsgVotePassed', '\c3Vote\c2: FAIR TEAMS ENABLED. Changes will take place next mission.'); + $Host::ClassicFairTeams = $FairTeams = 1; + %setto = "enabled"; + } + %cause = "(vote)"; + } + else + { + if($FairTeams) + messageAll('MsgVoteFailed', '\c2Disable fair teams vote did not pass: %1 percent.', mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount - %game.totalVotesNone) * 100)); + else + messageAll('MsgVoteFailed', '\c2Enable fair teams vote did not pass: %1 percent.', mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount - %game.totalVotesNone) * 100)); + } + } + if(%setto !$= "") + logEcho($AdminCl.nameBase @ ": fair teams "@%setto SPC %cause, 1); +} + +// z0dd - ZOD, 5/23/03. New function, vote for Armor limiting +function DefaultGame::voteArmorLimits(%game, %admin) +{ + %setto = ""; + %cause = ""; + if(%admin) + { + if($LimitArmors) + { + messageAll('MsgAdminForce', '\c2The Admin %1 has disabled armor limits.', $AdminCl.name); + $Host::ClassicLimitArmors = $LimitArmors = 0; + %setto = "disabled"; + } + else + { + messageAll('MsgAdminForce', '\c2The Admin %1 has enabled armor limits.', $AdminCl.name); + $Host::ClassicLimitArmors = $LimitArmors = 1; + resetArmorMaxes(); + $SpawnFavs = 0; + %setto = "enabled"; + } + %cause = "(admin)"; + } + if(%setto !$= "") + logEcho($AdminCl.nameBase @ ": armor limits "@%setto SPC %cause, 1); +} + +// z0dd - ZOD, 5/23/03. New function, clear server for tournament +function DefaultGame::voteClearServer(%game, %admin) +{ + if( %admin ) + { + if(isEventPending($ClearSchedule)) + cancel($ClearSchedule); + + centerPrintAll("\nSERVER WILL BE CLEARING FOR TOURNAMENT IN 10 SECONDS.", 5, 3); + messageAll('MsgAdminForce', '\c2The Admin %1 is clearing the server for a tournament, you will be kicked in 10 seconds.', $AdminCl.name); + $ClearSchedule = %game.schedule(10000, "clearserver", $AdminCl); + } +} + +function DefaultGame::clearserver(%game, %admin) +{ + if(isEventPending($ClearSchedule)) + { + cancel($ClearSchedule); + $ClearSchedule = ""; + } + logEcho(%admin.nameBase @ " has cleared the server for a tournament.", 1); + + for(%i = 0; %i < ClientGroup.getCount(); %i++) + { + %cl = ClientGroup.getObject(%i); + if(!%cl.isAdmin) + { + messageClient(%cl, 'onClientKicked', ""); + if(%cl.isAIControlled()) + { + $HostGameBotCount--; + %cl.drop(); + } + else + { + if(isObject(%cl.player)) + %cl.player.scriptKill(0); + + if(isObject(%cl)) + { + %cl.setDisconnectReason( "Clearing server for Tournament." ); + %cl.schedule(700, "delete"); + } + BanList::add(%cl.guid, "0", 120); + } + } + } +} + +function DefaultGame::voteSkipMission(%game, %admin, %arg1, %arg2, %arg3, %arg4) +{ + if(%admin) + { + messageAll('MsgAdminForce', '\c2The Admin %1 has skipped to the next mission.',$AdminCl.name ); + echo($AdminCl.nameBase @ ": mission skipped (admin)"); + %game.gameOver(); + //loadMission( findNextCycleMission(), $CurrentMissionType, false ); + cycleMissions(); + } + else + { + %totalVotes = %game.totalVotesFor + %game.totalVotesAgainst; + // Added people who dont vote into the equation, now if you do not vote, it doesn't count as a no. - z0dd - ZOD + if(%totalVotes > 0 && (%game.totalVotesFor / (ClientGroup.getCount() - $HostGameBotCount - %game.totalVotesNone)) > ($Host::VotePasspercent / 100)) + { + messageAll('MsgVotePassed', '\c2The mission was skipped to next by vote.'); + echo("mission skipped (vote)"); + %game.gameOver(); + //loadMission( findNextCycleMission(), $CurrentMissionType, false ); + cycleMissions(); + } + else + messageAll('MsgVoteFailed', '\c2Skip mission vote did not pass: %1 percent.', mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount - %game.totalVotesNone) * 100)); + } +} + +//------------------------------------------------------------------------------ +$ScoreHudMaxVisible = 19; +function DefaultGame::updateScoreHud(%game, %client, %tag) +{ + if (Game.numTeams > 1) + { + // Send header: + messageClient( %client, 'SetScoreHudHeader', "", '\t%1%2\t%3%4', + %game.getTeamName(1), $TeamScore[1], %game.getTeamName(2), $TeamScore[2] ); + + // Send subheader: + messageClient( %client, 'SetScoreHudSubheader', "", '\tPLAYERS (%1)SCORE\tPLAYERS (%2)SCORE', + $TeamRank[1, count], $TeamRank[2, count] ); + + %index = 0; + while ( true ) + { + if ( %index >= $TeamRank[1, count]+2 && %index >= $TeamRank[2, count]+2 ) + break; + + //get the team1 client info + %team1Client = ""; + %team1ClientScore = ""; + %col1Style = ""; + if ( %index < $TeamRank[1, count] ) + { + %team1Client = $TeamRank[1, %index]; + %team1ClientScore = %team1Client.score $= "" ? 0 : %team1Client.score; + %col1Style = %team1Client == %client ? "" : ""; + %team1playersTotalScore += %team1Client.score; + } + else if( %index == $teamRank[1, count] && $teamRank[1, count] != 0 && %game.class $= "CTFGame") // z0dd - ZOD, 9/29/02. Removed T2 demo code from here + { + %team1ClientScore = "--------------"; + } + else if( %index == $teamRank[1, count]+1 && $teamRank[1, count] != 0 && %game.class $= "CTFGame") // z0dd - ZOD, 9/29/02. Removed T2 demo code from here + { + %team1ClientScore = %team1playersTotalScore != 0 ? %team1playersTotalScore : 0; + } + //get the team2 client info + %team2Client = ""; + %team2ClientScore = ""; + %col2Style = ""; + if ( %index < $TeamRank[2, count] ) + { + %team2Client = $TeamRank[2, %index]; + %team2ClientScore = %team2Client.score $= "" ? 0 : %team2Client.score; + %col2Style = %team2Client == %client ? "" : ""; + %team2playersTotalScore += %team2Client.score; + } + else if( %index == $teamRank[2, count] && $teamRank[2, count] != 0 && %game.class $= "CTFGame") // z0dd - ZOD, 9/29/02. Removed T2 demo code from here + { + %team2ClientScore = "--------------"; + } + else if( %index == $teamRank[2, count]+1 && $teamRank[2, count] != 0 && %game.class $= "CTFGame") // z0dd - ZOD, 9/29/02. Removed T2 demo code from here + { + %team2ClientScore = %team2playersTotalScore != 0 ? %team2playersTotalScore : 0; + } + + //if the client is not an observer, send the message + if (%client.team != 0) + { + messageClient( %client, 'SetLineHud', "", %tag, %index, '\t%5%1%2\t%6%3%4', + %team1Client.name, %team1ClientScore, %team2Client.name, %team2ClientScore, %col1Style, %col2Style ); + } + //else for observers, create an anchor around the player name so they can be observed + else + { + messageClient( %client, 'SetLineHud', "", %tag, %index, '\t%5%1%2\t%6%3%4', + %team1Client.name, %team1ClientScore, %team2Client.name, %team2ClientScore, %col1Style, %col2Style, %team1Client, %team2Client ); + } + + %index++; + } + } + else + { + //tricky stuff here... use two columns if we have more than 15 clients... + %numClients = $TeamRank[0, count]; + if ( %numClients > $ScoreHudMaxVisible ) + %numColumns = 2; + + // Clear header: + messageClient( %client, 'SetScoreHudHeader', "", "" ); + + // Send header: + if (%numColumns == 2) + messageClient(%client, 'SetScoreHudSubheader', "", '\tPLAYERSCORE\tPLAYERSCORE'); + else + messageClient(%client, 'SetScoreHudSubheader', "", '\tPLAYERSCORE'); + + %countMax = %numClients; + if ( %countMax > ( 2 * $ScoreHudMaxVisible ) ) + { + if ( %countMax & 1 ) + %countMax++; + %countMax = %countMax / 2; + } + else if ( %countMax > $ScoreHudMaxVisible ) + %countMax = $ScoreHudMaxVisible; + + for ( %index = 0; %index < %countMax; %index++ ) + { + //get the client info + %col1Client = $TeamRank[0, %index]; + %col1ClientScore = %col1Client.score $= "" ? 0 : %col1Client.score; + %col1Style = %col1Client == %client ? "" : ""; + + //see if we have two columns + if ( %numColumns == 2 ) + { + %col2Client = ""; + %col2ClientScore = ""; + %col2Style = ""; + + //get the column 2 client info + %col2Index = %index + %countMax; + if ( %col2Index < %numClients ) + { + %col2Client = $TeamRank[0, %col2Index]; + %col2ClientScore = %col2Client.score $= "" ? 0 : %col2Client.score; + %col2Style = %col2Client == %client ? "" : ""; + } + } + + //if the client is not an observer, send the message + if (%client.team != 0) + { + if ( %numColumns == 2 ) + messageClient(%client, 'SetLineHud', "", %tag, %index, '\t%5%1%2\t%6%3%4', + %col1Client.name, %col1ClientScore, %col2Client.name, %col2ClientScore, %col1Style, %col2Style ); + else + messageClient( %client, 'SetLineHud', "", %tag, %index, '\t%3%1%2', + %col1Client.name, %col1ClientScore, %col1Style ); + } + //else for observers, create an anchor around the player name so they can be observed + else + { + if ( %numColumns == 2 ) + messageClient(%client, 'SetLineHud', "", %tag, %index, '\t%5%1%2\t%6%3%4', + %col1Client.name, %col1ClientScore, %col2Client.name, %col2ClientScore, %col1Style, %col2Style, %col1Client, %col2Client ); + else + messageClient( %client, 'SetLineHud', "", %tag, %index, '\t%3%1%2', + %col1Client.name, %col1ClientScore, %col1Style, %col1Client ); + } + } + + } + + // Tack on the list of observers: + %observerCount = 0; + for (%i = 0; %i < ClientGroup.getCount(); %i++) + { + %cl = ClientGroup.getObject(%i); + if (%cl.team == 0) + %observerCount++; + } + + if (%observerCount > 0) + { + messageClient( %client, 'SetLineHud', "", %tag, %index, ""); + %index++; + messageClient(%client, 'SetLineHud', "", %tag, %index, '\tOBSERVERS (%1)TIME', %observerCount); + %index++; + for (%i = 0; %i < ClientGroup.getCount(); %i++) + { + %cl = ClientGroup.getObject(%i); + //if this is an observer + if (%cl.team == 0) + { + %obsTime = getSimTime() - %cl.observerStartTime; + %obsTimeStr = %game.formatTime(%obsTime, false); + messageClient( %client, 'SetLineHud', "", %tag, %index, '\t%1%2', + %cl.name, %obsTimeStr ); + %index++; + } + } + } + + //clear the rest of Hud so we don't get old lines hanging around... + messageClient( %client, 'ClearHud', "", %tag, %index ); +} + +//------------------------------------------------------------------------------ +function UpdateClientTimes(%time) +{ + %secondsLeft = %time / 1000; + messageAll('MsgSystemClock', "", (%secondsLeft / 60), %time); +} + +//------------------------------------------------------------------------------ +function notifyMatchStart(%time) +{ + %seconds = mFloor(%time / 1000); + if (%seconds > 2) + MessageAll('MsgMissionStart', '\c2Match starts in %1 seconds.~wfx/misc/hunters_%1.wav', %seconds); + else if (%seconds == 2) + MessageAll('MsgMissionStart', '\c2Match starts in 2 seconds.~wvoice/announcer/ann.match_begins.wav'); + else if (%seconds == 1) + MessageAll('MsgMissionStart', '\c2Match starts in 1 second.'); + UpdateClientTimes(%time); +} + +//------------------------------------------------------------------------------ +function notifyMatchEnd(%time) +{ + %seconds = mFloor(%time / 1000); + + if (%seconds > 1) { + MessageAll('MsgMissionEnd', '\c2Match ends in %1 seconds.~wfx/misc/hunters_%1.wav', %seconds); + if (%seconds > 59) { + MessageAll('MsgNotifyEvoNextMission', '\c2Next Mission: \c3%1', $EvoCachedNextMission); + } + } + else if (%seconds == 1) + MessageAll('MsgMissionEnd', '\c2Match ends in 1 second.~wfx/misc/hunters_1.wav'); + + UpdateClientTimes(%time); +} + +function notifyMatchEndMinutes(%time) +{ + %seconds = mFloor(%time / 1000); + + if (%seconds == 180) + MessageAll('MsgMissionEndMinutes', '\c2Match ends in 3 minutes.~wfx/misc/flagself.wav'); + else if (%seconds == 120) + MessageAll('MsgMissionEndMinutes', '\c2Match ends in 2 minutes.~wfx/misc/flagself.wav'); + + UpdateClientTimes(%time); +} + +function DefaultGame::formatTime(%game, %tStr, %includeHundredths) +{ + %timeInSeconds = %tStr / 1000; + %mins = mFloor(%timeInSeconds / 60); + if(%mins < 1) + %timeString = "00:"; + else if(%mins < 10) + %timeString = "0" @ %mins @ ":"; + else + %timeString = %mins @ ":"; + + %timeInSeconds -= (%mins * 60); + %secs = mFloor(%timeInSeconds); + if(%secs < 1) + %timeString = %timeString @ "00"; + else if(%secs < 10) + %timeString = %timeString @ "0" @ %secs; + else + %timeString = %timeString @ %secs; + + if (%includeHundredths) + { + %timeString = %timeString @ "."; + %timeInSeconds -= %secs; + %hSecs = mFloor(%timeInSeconds * 100); // will be between 0 and 999 + if(%hSecs < 1) + %timeString = %timeString @ "00"; + else if(%hSecs < 10) + %timeString = %timeString @ "0" @ %hSecs; + else + %timeString = %timeString @ %hSecs; + } + + return %timeString; +} + +//------------------------------------------------------------------------------ +//AI FUNCTIONS +function DefaultGame::AIChooseGameObjective(%game, %client) +{ + AIChooseObjective(%client); +} + +//------------------------------------------------------------------------------ +function DefaultGame::getServerStatusString(%game) +{ + %status = %game.numTeams; + for ( %team = 1; %team - 1 < %game.numTeams; %team++ ) + { + %score = isObject( $teamScore[%team] ) ? $teamScore[%team] : 0; + %teamStr = getTaggedString( %game.getTeamName(%team) ) TAB %score; + %status = %status NL %teamStr; + } + + %status = %status NL ClientGroup.getCount(); + for ( %i = 0; %i < ClientGroup.getCount(); %i++ ) + { + %cl = ClientGroup.getObject( %i ); + %score = %cl.score $= "" ? 0 : %cl.score; + %playerStr = getTaggedString( %cl.name ) TAB getTaggedString( %game.getTeamName(%cl.team) ) TAB %score; + %status = %status NL %playerStr; + } + return( %status ); +} + +//------------------------------------------------------------------------------ +function DefaultGame::OptionsDlgSleep( %game ) +{ + // ignore in the default game... +} + +//------------------------------------------------------------------------------ +function DefaultGame::endMission( %game ) +{ +} + +//------------------------------------------------------------------------------ +// z0dd - ZOD. Console spam fix +function DefaultGame::countFlips(%game) +{ + return false; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// Random Teams code by Founder (founder@mechina.com) 6/22/02 /////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////// + +function DefaultGame::setupClientTeams(%game) +{ + if(!$RandomTeams || %game.numTeams == 1) + { + %count = ClientGroup.getCount(); + for (%i = 0; %i < %count; %i++) + { + %client = ClientGroup.getObject(%i); + %client.lastTeam = %client.team; + %client.setupTeam = 0; + } + return; + } + else + { + %numTeamPlayers = 0; + %totalNumPlayers = ClientGroup.getCount(); + for(%i = 0; %i < %totalNumPlayers; %i++) + { + %cl = ClientGroup.getObject(%i); + if(%cl.team == 0) + %cl.lastTeam = %cl.team; + else + { + %teamPlayer[%numTeamPlayers] = %cl; + %numTeamPlayers++; + } + } + %numPlayersLeft = %numTeamPlayers - 1; + for(%j = 0; %j < %numTeamPlayers; %j++) + { + if(%numPlayersLeft > 0) + { + %r = 0; + %val = mFloor(getRandom(0, %numPlayersLeft)); + if(%val > %numPlayersLeft) + %val = %numPlayersLeft; + + %client = %teamPlayer[%val]; + %shuffledPlayersArray[%j] = %client; + for(%y = 0; %y <= %numPlayersLeft; %y++) + { + %clplyr = %teamPlayer[%y]; + if(%clplyr != %client) + { + %teamPlayer[%r] = %clplyr; + %r++; + } + } + %numPlayersLeft--; + } + else + %shuffledPlayersArray[%j] = %teamPlayer[%numPlayersLeft]; + } + %thisTeam = 1; + for(%k = 0; %k <= %numTeamPlayers; %k++) + { + if(%thisTeam == 1) + { + %shuffledPlayersArray[%k].lastTeam = 1; + %thisTeam = 0; + } + else + { + %shuffledPlayersArray[%k].lastTeam = 2; + %thisTeam = 1; + } + } + } +} diff --git a/inventory.cs b/inventory.cs new file mode 100644 index 0000000..b542342 --- /dev/null +++ b/inventory.cs @@ -0,0 +1,634 @@ +//---------------------------------------------------------------------------- + +// Item Datablocks +// image = Name of mounted image datablock +// onUse(%this,%object) + +// Item Image Datablocks +// item = Name of item inventory datablock + +// ShapeBase Datablocks +// max[Item] = Maximum amount that can be caried + +// ShapeBase Objects +// inv[Item] = Count of item in inventory +//---------------------------------------------------------------------------- + +$TestCheats = 0; + +function serverCmdUse(%client,%data) +{ + // Item names from the client must converted + // into DataBlocks + // %data = ItemDataBlock[%item]; + if(isObject(%client.player)) // z0dd - ZOD, 5/18/03. Console spam fix + %client.getControlObject().use(%data); +} + +function serverCmdThrow(%client,%data) +{ + // Item names from the client must converted + // into DataBlocks + // %data = ItemDataBlock[%item]; + + //----------------------------------------------------------------------- + // z0dd - ZOD, 4/18/02. Let one keybind handle all grenade types. + if(isObject(%client.player)) // z0dd - ZOD, 5/18/03. Console spam fix + { + if(%data $= Grenade) + { + // figure out which grenade type you're using + for(%x = 0; $InvGrenade[%x] !$= ""; %x++) + { + if(%client.getControlObject().inv[$NameToInv[$InvGrenade[%x]]] > 0) + { + %data = $NameToInv[$InvGrenade[%x]]; + break; + } + } + %client.getControlObject().throw(%data); + } + else if(%data $= Mine) + { + for(%x = 0; $InvMine[%x] !$= ""; %x++) + { + if(%client.getControlObject().inv[$NameToInv[$InvMine[%x]]] > 0) + { + %data = $NameToInv[$InvMine[%x]]; + break; + } + } + %client.getControlObject().throw(%data); + } + else if(%data $= "Ammo") + { + %weapon = %client.getControlObject().getMountedImage($WeaponSlot); + if(%weapon !$= "") + { + if(%weapon.ammo !$= "") + %client.getControlObject().throw(%weapon.ammo); + else + return; + } + } + else + %client.getControlObject().throw(%data); + } +} + +function serverCmdThrowWeapon(%client,%data) +{ + // Item names from the client must converted + // into DataBlocks + // %data = ItemDataBlock[%item]; + if(isObject(%client.player)) // z0dd - ZOD, 5/18/03. Console spam fix + %client.getControlObject().throwWeapon(); +} + +function serverCmdThrowPack(%client,%data) +{ + if(isObject(%client.player)) // z0dd - ZOD, 5/18/03. Console spam fix + %client.getControlObject().throwPack(); +} + +function serverCmdTogglePack(%client,%data) +{ + // this function is apparently never called + %client.getControlObject().togglePack(); +} + +function serverCmdThrowFlag(%client) +{ + //Game.playerDroppedFlag(%client.player); + Game.dropFlag(%client.player); +} + +function serverCmdSelectWeaponSlot( %client, %data ) +{ + if(isObject(%client.player)) // z0dd - ZOD, 5/18/03. Console spam fix + %client.getControlObject().selectWeaponSlot( %data ); +} + +function serverCmdCycleWeapon( %client, %data ) +{ + if(isObject(%client.player)) // z0dd - ZOD, 5/18/03. Console spam fix + %client.getControlObject().cycleWeapon( %data ); +} + +function serverCmdStartThrowCount(%client, %data) +{ + %client.player.throwStart = getSimTime(); +} + +$maxThrowStr = 2.2; // z0dd - ZOD, 8/6/02. New throw str features + +function serverCmdEndThrowCount(%client, %data) +{ + if(%client.player.throwStart == 5) + return; + + // --------------------------------------------------------------- + // z0dd - ZOD, 8/6/02. New throw str features + %throwStrength = (getSimTime() - %client.player.throwStart) / 150; + if(%throwStrength > $maxThrowStr) + %throwStrength = $maxThrowStr; + else if(%throwStrength < 0.5) + %throwStrength = 0.5; + // --------------------------------------------------------------- + + %throwScale = %throwStrength / 2; + %client.player.throwStrength = %throwScale; + + %client.player.throwStart = 5; +} + +// -------------------------------------------------------------------- +// z0dd - ZOD, 9/27/02. No buildup power. Rewrote function +function serverCmdthrowMaxEnd(%client, %data) +{ + %client.player.throwStrength = $maxThrowStr / 2; +} +// -------------------------------------------------------------------- + +function ShapeBase::throwWeapon(%this) +{ + if(Game.shapeThrowWeapon(%this)) { + %image = %this.getMountedImage($WeaponSlot); + %this.throw(%image.item); + %this.client.setWeaponsHudItem(%image.item, 0, 0); + } +} + +function ShapeBase::throwPack(%this) +{ + if(isObject(%this)) // z0dd - ZOD, 5/18/03. Console spam fix + { + %image = %this.getMountedImage($BackpackSlot); + %this.throw(%image.item); + %this.client.setBackpackHudItem(%image.item, 0); + } +} + +function ShapeBase::throw(%this,%data) +{ + if(!isObject(%data)) + return false; + + if (%this.inv[%data.getName()] > 0) { + + // save off the ammo count on this item + if( %this.getInventory( %data ) < $AmmoIncrement[%data.getName()] ) + %data.ammoStore = %this.getInventory( %data ); + else + %data.ammoStore = $AmmoIncrement[%data.getName()]; + + // Throw item first... + %this.throwItem(%data); + if($AmmoIncrement[%data.getName()] !$= "") + %this.decInventory(%data,$AmmoIncrement[%data.getName()]); + else + %this.decInventory(%data,1); + return true; + } + return false; +} + +function ShapeBase::use(%this, %data) +{ + //if(%data.class $= "Weapon") { + // error("ShapeBase::use " @ %data); + //} + if(isObject(%this) && %data !$= "") // z0dd - ZOD, 5/18/03. Console spam fix + { + if(%data $= Grenade) + { + // figure out which grenade type you're using + for(%x = 0; $InvGrenade[%x] !$= ""; %x++) { + if(%this.inv[$NameToInv[$InvGrenade[%x]]] > 0) + { + %data = $NameToInv[$InvGrenade[%x]]; + break; + } + } + } + else if(%data $= Mine) // z0dd - ZOD, 5/18/03. For more mine types + { + // figure out which mine type you're using + for(%m = 0; $InvMine[%m] !$= ""; %m++) { + if(%this.inv[$NameToInv[$InvMine[%m]]] > 0) + { + %data = $NameToInv[$InvMine[%m]]; + break; + } + } + } + else if(%data $= "Backpack") { + %pack = %this.getMountedImage($BackpackSlot); + // if you don't have a pack but have placed a satchel charge, detonate it + if(!%pack && (%this.thrownChargeId > 0) && %this.thrownChargeId.armed ) + { + %this.playAudio( 0, SatchelChargeExplosionSound ); + schedule( 600, %this, "detonateSatchelCharge", %this ); // z0dd - ZOD, 8/24/02. Time after pressing fire that satchel blows. Was 800 + return true; + } + return false; + } + else if(%data $= Beacon) + { + %data.onUse(%this); + if (%this.inv[%data.getName()] > 0) + return true; + } + + // default case + if (%this.inv[%data.getName()] > 0) { + %data.onUse(%this); + return true; + } + return false; + } +} + +function ShapeBase::pickup(%this,%obj,%amount) +{ + %data = %obj.getDatablock(); + %delta = %this.incInventory(%data,%amount); + + if (%delta) + %data.onPickup(%obj,%this,%delta); + return %delta; +} + +function ShapeBase::hasInventory(%this, %data) +{ + // changed because it was preventing weapons cycling correctly (MES) + return (%this.inv[%data] > 0); +} + +function ShapeBase::maxInventory(%this, %data) +{ + //error("ShapeBase::maxInventory( " @ %this.client.nameBase @ ", " @ %data @ " )"); + if(isObject(%data)) // z0dd - ZOD, 5/18/03. Console spam fix + { + if($TestCheats) + return 999; + else + return %this.getDatablock().max[%data.getName()]; + } +} + +function ShapeBase::incInventory(%this, %data, %amount) +{ + if(isObject(%data)) // z0dd - ZOD, 5/18/03. Console spam fix + { + %max = %this.maxInventory(%data); + %cv = %this.inv[%data.getName()]; + if (%cv < %max) { + if (%cv + %amount > %max) + %amount = %max - %cv; + + %this.setInventory(%data,%cv + %amount); + %data.incCatagory(%this); // Inc the players weapon count + return %amount; + } + return 0; + } +} + +function ShapeBase::decInventory(%this,%data,%amount) +{ + %name = %data.getName(); + %cv = %this.inv[%name]; + if (%cv > 0) { + if (%cv < %amount) + %amount = %cv; + %this.setInventory(%data,%cv - %amount, true); + %data.decCatagory(%this); // Dec the players weapon count + return %amount; + } + return 0; +} + +function SimObject::decCatagory(%this) +{ + //function was added to reduce console err msg spam +} + +function SimObject::incCatagory(%this) +{ + //function was added to reduce console err msg spam +} + +function ShapeBase::setInventory(%this,%data,%value,%force) +{ + if (!isObject(%data)) + return; + + %name = %data.getName(); + if (%value < 0) + %value = 0; + else + { + if (!%force) + { + // Impose inventory limits + %max = %this.maxInventory(%data); + if (%value > %max) + %value = %max; + } + } + if (%this.inv[%name] != %value) + { + %this.inv[%name] = %value; + %data.onInventory(%this,%value); + + if ( %data.className $= "Weapon" ) + { + if ( %this.weaponSlotCount $= "" ) + %this.weaponSlotCount = 0; + + %cur = -1; + for ( %slot = 0; %slot < %this.weaponSlotCount; %slot++ ) + { + if ( %this.weaponSlot[%slot] $= %name ) + { + %cur = %slot; + break; + } + } + + if ( %cur == -1 ) + { + // Put this weapon in the next weapon slot: + if ( %this.weaponSlot[%this.weaponSlotCount - 1] $= "TargetingLaser" ) + { + %this.weaponSlot[%this.weaponSlotCount - 1] = %name; + %this.weaponSlot[%this.weaponSlotCount] = "TargetingLaser"; + } + else + %this.weaponSlot[%this.weaponSlotCount] = %name; + %this.weaponSlotCount++; + } + else + { + // Remove the weapon from the weapon slot: + for ( %i = %cur; %i < %this.weaponSlotCount - 1; %i++ ) + %this.weaponSlot[%i] = %this.weaponSlot[%i + 1]; + %this.weaponSlot[%i] = ""; + %this.weaponSlotCount--; + } + } + + %this.getDataBlock().onInventory(%data,%value); + } + return %value; +} + +function ShapeBase::getInventory(%this,%data) +{ + if ( isObject( %data ) ) + return( %this.inv[%data.getName()] ); + else + return( 0 ); +} + +// z0dd - ZOD, 9/13/02. Streamlined. +function ShapeBase::hasAmmo( %this, %weapon ) +{ + if(%weapon $= LaserRifle) + return( %this.getInventory( EnergyPack ) ); + + if (%weapon.image.ammo $= "") + { + if (%weapon $= TargetingLaser) + { + return( false ); + } + else + { + return( true ); + } + } + else + { + return( %this.getInventory( %weapon.image.ammo ) > 0 ); + } +} + +function SimObject::onInventory(%this, %obj) +{ + //function was added to reduce console error msg spam +} + +function ShapeBase::throwItem(%this,%data) +{ + %item = new Item() { + dataBlock = %data; + rotation = "0 0 1 " @ (getRandom() * 360); + }; + + %item.ammoStore = %data.ammoStore; + MissionCleanup.add(%item); + %this.throwObject(%item); +} + +function ShapeBase::throwObject(%this,%obj) +{ + //------------------------------------------------------------------ + // z0dd - ZOD, 4/15/02. Allow respawn switching during tourney wait. + if(!$MatchStarted) + return; + //------------------------------------------------------------------ + + // z0dd - ZOD, 5/26/02. Remove anti-hover so flag can be thrown properly + if(%obj.getDataBlock().getName() $= "Flag") + { + %obj.static = false; + // z0dd - ZOD - SquirrelOfDeath, 10/02/02. Hack for flag collision bug. + if(Game.Class $= CTFGame || Game.Class $= PracticeCTFGame) + %obj.searchSchedule = Game.schedule(10, "startFlagCollisionSearch", %obj); + } + //------------------------------------------------------------------ + + %srcCorpse = (%this.getState() $= "Dead"); // z0dd - ZOD, 4/14/02. Flag tossed from corpse + //if the object is being thrown by a corpse, use a random vector + if (%srcCorpse && %obj.getDataBlock().getName() !$= "Flag") // z0dd - ZOD, 4/14/02. Except for flags.. + { + %vec = (-1.0 + getRandom() * 2.0) SPC (-1.0 + getRandom() * 2.0) SPC getRandom(); + %vec = vectorScale(%vec, 10); + } + else // else Initial vel based on the dir the player is looking + { + %eye = %this.getEyeVector(); + %vec = vectorScale(%eye, 20); + } + + // Add a vertical component to give the item a better arc + %dot = vectorDot("0 0 1",%eye); + if (%dot < 0) + %dot = -%dot; + %vec = vectorAdd(%vec,vectorScale("0 0 12",1 - %dot)); // z0dd - ZOD, 9/10/02. 10 was 8 + + // Add player's velocity + %vec = vectorAdd(%vec,%this.getVelocity()); + %pos = getBoxCenter(%this.getWorldBox()); + + //since flags have a huge mass (so when you shoot them, they don't bounce too far) + //we need to up the %vec so that you can still throw them... + if (%obj.getDataBlock().getName() $= "Flag") + { + %vec = vectorScale(%vec, (%srcCorpse ? 40 : 75)); // z0dd - ZOD, 4/14/02. Throw flag force. Value was 40 + // ------------------------------------------------------------ + // z0dd - ZOD, 9/27/02. Delay on grabbing flag after tossing it + %this.flagTossWait = true; + %this.schedule(1000, resetFlagTossWait); + // ------------------------------------------------------------ + } + + // + %obj.setTransform(%pos); + %obj.applyImpulse(%pos,%vec); + %obj.setCollisionTimeout(%this); + %data = %obj.getDatablock(); + + %data.onThrow(%obj,%this); + + //call the AI hook + AIThrowObject(%obj); +} + +function ShapeBase::clearInventory(%this) +{ + // z0dd - ZOD, 5/18/03. Auto cleanup of weapons and ammo. Streamline + for(%i = 0; %i < $WeaponsHudCount; %i++) + { + %this.setInventory($WeaponsHudData[%i, itemDataName], 0); + if($WeaponsHudData[%i, ammoDataName] !$= "") + %this.setInventory($WeaponsHudData[%i, ammoDataName], 0); + } + for(%i = 0; $InvGrenade[%i] !$= ""; %i++) + %this.setInventory($NameToInv[$InvGrenade[%i]], 0); + + for(%i = 0; $InvMine[%i] !$= ""; %i++) + %this.setInventory($NameToInv[$InvMine[%i]], 0); + + %this.setInventory(RepairKit, 0); + %this.setInventory(Beacon, 0); + + // take away any pack the player has + %curPack = %this.getMountedImage($BackpackSlot); + if(%curPack > 0) + %this.setInventory(%curPack.item, 0); +} + +//---------------------------------------------------------------------------- +function ShapeBase::cycleWeapon( %this, %data ) +{ + if ( %this.weaponSlotCount == 0 ) + return; + + %slot = -1; + if ( %this.getMountedImage($WeaponSlot) != 0 ) + { + %curWeapon = %this.getMountedImage($WeaponSlot).item.getName(); + for ( %i = 0; %i < %this.weaponSlotCount; %i++ ) + { + //error("curWeaponName == " @ %curWeaponName); + if ( %curWeapon $= %this.weaponSlot[%i] ) + { + %slot = %i; + break; + } + } + } + + if ( %data $= "prev" ) + { + // Previous weapon... + if ( %slot == 0 || %slot == -1 ) + { + %i = %this.weaponSlotCount - 1; + %slot = 0; + } + else + %i = %slot - 1; + } + else + { + // Next weapon... + if ( %slot == ( %this.weaponSlotCount - 1 ) || %slot == -1 ) + { + %i = 0; + %slot = ( %this.weaponSlotCount - 1 ); + } + else + %i = %slot + 1; + } + + %newSlot = -1; + while ( %i != %slot ) + { + if ( %this.weaponSlot[%i] !$= "" + && %this.hasInventory( %this.weaponSlot[%i] ) + && %this.hasAmmo( %this.weaponSlot[%i] ) ) + { + // player has this weapon and it has ammo or doesn't need ammo + %newSlot = %i; + break; + } + + if ( %data $= "prev" ) + { + if ( %i == 0 ) + %i = %this.weaponSlotCount - 1; + else + %i--; + } + else + { + if ( %i == ( %this.weaponSlotCount - 1 ) ) + %i = 0; + else + %i++; + } + } + + if ( %newSlot != -1 ) + %this.use( %this.weaponSlot[%newSlot] ); +} + +//---------------------------------------------------------------------------- +function ShapeBase::selectWeaponSlot( %this, %data ) +{ + if ( %data < 0 || %data > %this.weaponSlotCount + || %this.weaponSlot[%data] $= "" || %this.weaponSlot[%data] $= "TargetingLaser" ) + return; + + %this.use( %this.weaponSlot[%data] ); +} + +//---------------------------------------------------------------------------- + +function serverCmdGiveAll(%client) +{ + if($TestCheats) + { + %player = %client.player; + // z0dd - ZOD, 5/18/03. Auto increment of weapons and ammo. Streamline + for(%i = 0; %i < $WeaponsHudCount; %i++) + { + %player.setInventory($WeaponsHudData[%i, itemDataName], 1); + if($WeaponsHudData[%i, ammoDataName] !$= "") + %player.setInventory($WeaponsHudData[%i, ammoDataName], 999); + } + for(%i = 0; $InvGrenade[%i] !$= ""; %i++) + %player.setInventory($NameToInv[$InvGrenade[%i]], 0); + + for(%i = 0; $InvMine[%i] !$= ""; %i++) + %player.setInventory($NameToInv[$InvMine[%i]], 0); + + %player.setInventory(RepairKit, 999); + %player.setInventory(Beacon, 999); + %player.setInventory(RocketCannonAmmo, 999); + } +} diff --git a/inventoryHud.cs b/inventoryHud.cs new file mode 100644 index 0000000..7be6b2e --- /dev/null +++ b/inventoryHud.cs @@ -0,0 +1,1171 @@ +//------------------------------------------------------------------------------ +function setUpFavPrefs() +{ + if($pref::FavCurrentSelect $= "") + $pref::FavCurrentSelect = 0; + for(%i = 0; %i < 10; %i++) + { + if($pref::FavNames[%i] $= "") + $pref::FavNames[%i] = "Favorite " @ %i + 1; + if($pref::Favorite[%i] $= "") + $pref::Favorite[%i] = "armor\tLight Armor"; + } + if($pref::FavCurrentList $= "") + $pref::FavCurrentList = 0; +} + +$FavCurrent = 0; +setUpFavPrefs(); + +$InvArmor[0] = "Scout"; +$InvArmor[1] = "Assault"; +$InvArmor[2] = "Juggernaut"; + +$NameToInv["Scout"] = "Light"; +$NameToInv["Assault"] = "Medium"; +$NameToInv["Juggernaut"] = "Heavy"; + + +$InvWeapon[0] = "Blaster"; +$InvWeapon[1] = "Plasma Rifle"; +$InvWeapon[2] = "Chaingun"; +$InvWeapon[3] = "Spinfusor"; +$InvWeapon[4] = "Grenade Launcher"; +$InvWeapon[5] = "Laser Rifle"; +$InvWeapon[6] = "ELF Projector"; +$InvWeapon[7] = "Fusion Mortar"; +$InvWeapon[8] = "Missile Launcher"; +$InvWeapon[9] = "Shocklance"; +//$InvWeapon[10] = "Targeting Laser"; + +// ------------------------------------- +// z0dd - ZOD, 9/12/02. TR2 need +$InvWeapon[10] = "TR2 Spinfusor"; +$InvWeapon[11] = "TR2 Grenade Launcher"; +$InvWeapon[12] = "TR2 Chaingun"; +$InvWeapon[13] = "TR2 Shocklance"; +$InvWeapon[14] = "TR2 Mortar"; +// ------------------------------------- + +$NameToInv["Blaster"] = "Blaster"; +$NameToInv["Plasma Rifle"] = "Plasma"; +$NameToInv["Chaingun"] = "Chaingun"; +$NameToInv["Spinfusor"] = "Disc"; +$NameToInv["Grenade Launcher"] = "GrenadeLauncher"; +$NameToInv["Laser Rifle"] = "SniperRifle"; +$NameToInv["ELF Projector"] = "ELFGun"; +$NameToInv["Fusion Mortar"] = "Mortar"; +$NameToInv["Missile Launcher"] = "MissileLauncher"; +$NameToInv["Shocklance"] = "ShockLance"; +//$NameToInv["Targeting Laser"] = "TargetingLaser"; + +// ------------------------------------------------------- +// z0dd - ZOD, 9/12/02. TR2 need +$NameToInv["TR2 Spinfusor"] = "TR2Disc"; +$NameToInv["TR2 Grenade Launcher"] = "TR2GrenadeLauncher"; +$NameToInv["TR2 Chaingun"] = "TR2Chaingun"; +$NameToInv["TR2 Energy Pack"] = "TR2EnergyPack"; +$NameToInv["TR2 Shocklance"] = "TR2Shocklance"; +$NameToInv["TR2 Mortar"] = "TR2Mortar"; +// ------------------------------------------------------- + + +$InvPack[0] = "Energy Pack"; +$InvPack[1] = "Repair Pack"; +$InvPack[2] = "Shield Pack"; +$InvPack[3] = "Cloak Pack"; +$InvPack[4] = "Sensor Jammer Pack"; +$InvPack[5] = "Ammunition Pack"; +$InvPack[6] = "Satchel Charge"; +$InvPack[7] = "Motion Sensor Pack"; +$InvPack[8] = "Pulse Sensor Pack"; +$InvPack[9] = "Inventory Station"; +$InvPack[10] = "Landspike Turret"; +$InvPack[11] = "Spider Clamp Turret"; +$InvPack[12] = "ELF Turret Barrel"; +$InvPack[13] = "Mortar Turret Barrel"; +$InvPack[14] = "Plasma Turret Barrel"; +$InvPack[15] = "AA Turret Barrel"; +$InvPack[16] = "Missile Turret Barrel"; +$InvPack[17] = "TR2 Energy Pack"; // z0dd - ZOD, 9/12/02. TR2 need + +// non-team mission pack choices (DM, Hunters, Rabbit) + +// z0dd - ZOD, 7/16/03. These are not used for anything, remove, save some bytes of ram +//$NTInvPack[0] = "Energy Pack"; +//$NTInvPack[1] = "Repair Pack"; +//$NTInvPack[2] = "Shield Pack"; +//$NTInvPack[3] = "Cloak Pack"; +//$NTInvPack[4] = "Sensor Jammer Pack"; +//$NTInvPack[5] = "Ammunition Pack"; +//$NTInvPack[6] = "Satchel Charge"; +//$NTInvPack[7] = "Motion Sensor Pack"; +//$NTInvPack[8] = "Pulse Sensor Pack"; +//$NTInvPack[9] = "Inventory Station"; +//$NTInvPack[10] = "TR2 Energy Pack"; // z0dd - ZOD, 9/12/02. TR2 need + +$NameToInv["Energy Pack"] = "EnergyPack"; +$NameToInv["Repair Pack"] = "RepairPack"; +$NameToInv["Shield Pack"] = "ShieldPack"; +$NameToInv["Cloak Pack"] = "CloakingPack"; +$NameToInv["Sensor Jammer Pack"] = "SensorJammerPack"; +$NameToInv["Ammunition Pack"] = "AmmoPack"; +$NameToInv["Satchel Charge"] = "SatchelCharge"; +$NameToInv["Motion Sensor Pack"] = "MotionSensorDeployable"; +$NameToInv["Pulse Sensor Pack"] = "PulseSensorDeployable"; +$NameToInv["Inventory Station"] = "InventoryDeployable"; +$NameToInv["Landspike Turret"] = "TurretOutdoorDeployable"; +$NameToInv["Spider Clamp Turret"] = "TurretIndoorDeployable"; +$NameToInv["ELF Turret Barrel"] = "ELFBarrelPack"; +$NameToInv["Mortar Turret Barrel"] = "MortarBarrelPack"; +$NameToInv["Plasma Turret Barrel"] = "PlasmaBarrelPack"; +$NameToInv["AA Turret Barrel"] = "AABarrelPack"; +$NameToInv["Missile Turret Barrel"] = "MissileBarrelPack"; + +$InvGrenade[0] = "Grenade"; +$InvGrenade[1] = "Whiteout Grenade"; +$InvGrenade[2] = "Concussion Grenade"; +$InvGrenade[3] = "Flare Grenade"; +$InvGrenade[4] = "Deployable Camera"; +$InvGrenade[5] = "TR2Grenade"; // z0dd - ZOD, 9/12/02. TR2 need + +$NameToInv["Grenade"] = "Grenade"; +$NameToInv["Whiteout Grenade"] = "FlashGrenade"; +$NameToInv["Concussion Grenade"] = "ConcussionGrenade"; +$NameToInv["Flare Grenade"] = "FlareGrenade"; +$NameToInv["Deployable Camera"] = "CameraGrenade"; +$NameToInv["TR2Grenade"] = "TR2Grenade"; // z0dd - ZOD, 9/12/02. TR2 need + + +$InvMine[0] = "Mine"; + +$NameToInv["Mine"] = "Mine"; + +//$InvBanList[DeployInv, "ElfBarrelPack"] = 1; +//$InvBanList[DeployInv, "MortarBarrelPack"] = 1; +//$InvBanList[DeployInv, "PlasmaBarrelPack"] = 1; +//$InvBanList[DeployInv, "AABarrelPack"] = 1; +//$InvBanList[DeployInv, "MissileBarrelPack"] = 1; +$InvBanList[DeployInv, "InventoryDeployable"] = 1; + +//------------------------------------------------------------------------------ +function InventoryScreen::loadHud( %this, %tag ) +{ + $Hud[%tag] = InventoryScreen; + $Hud[%tag].childGui = INV_Root; + $Hud[%tag].parent = INV_Root; +} + +//------------------------------------------------------------------------------ +function InventoryScreen::setupHud( %this, %tag ) +{ + %favListStart = $pref::FavCurrentList * 10; + %this.selId = $pref::FavCurrentSelect - %favListStart + 1; + + // Add the list menu: + $Hud[%tag].staticData[0, 0] = new ShellPopupMenu(INV_ListMenu) + { + profile = "ShellPopupProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "16 313"; + extent = "170 36"; + minExtent = "8 8"; + visible = "1"; + setFirstResponder = "0"; + modal = "1"; + helpTag = "0"; + maxPopupHeight = "220"; + text = ""; + }; + + // Add favorite tabs: + for( %i = 0; %i < 10; %i++ ) + { + %yOffset = ( %i * 30 ) + 10; + $Hud[%tag].staticData[0, %i + 1] = new ShellTabButton() { + profile = "ShellTabProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "4 " @ %yOffset; + extent = "206 38"; + minExtent = "8 8"; + visible = "1"; + setFirstResponder = "0"; + modal = "1"; + helpTag = "0"; + command = "InventoryScreen.onTabSelect(" @ %favListStart + %i @ ");"; + text = strupr( $pref::FavNames[%favListStart + %i] ); + }; + $Hud[%tag].staticData[0, %i + 1].setValue( ( %favListStart + %i ) == $pref::FavCurrentSelect ); + + $Hud[%tag].parent.add( $Hud[%tag].staticData[0, %i + 1] ); + } + + %text = "Favorites " @ %favListStart + 1 SPC "-" SPC %favListStart + 10; + $Hud[%tag].staticData[0, 0].onSelect( $pref::FavCurrentList, %text, true ); + + $Hud[%tag].parent.add( $Hud[%tag].staticData[0, 0] ); + + // Add the SAVE button: + $Hud[%tag].staticData[1, 0] = new ShellBitmapButton() + { + profile = "ShellButtonProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "409 295"; + extent = "75 38"; + minExtent = "8 8"; + visible = "1"; + setFirstResponder = "0"; + modal = "1"; + helpTag = "0"; + command = "saveFavorite();"; + text = "SAVE"; + }; + + // Add the name edit control: + $Hud[%tag].staticData[1, 1] = new ShellTextEditCtrl() + { + profile = "NewTextEditProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "217 295"; + extent = "196 38"; + minExtent = "8 8"; + visible = "1"; + altCommand = "saveFavorite()"; + setFirstResponder = "1"; + modal = "1"; + helpTag = "0"; + historySize = "0"; + maxLength = "16"; + }; + + $Hud[%tag].staticData[1, 1].setValue( $pref::FavNames[$pref::FavCurrentSelect] ); + + $Hud[%tag].parent.add( $Hud[%tag].staticData[1, 0] ); + $Hud[%tag].parent.add( $Hud[%tag].staticData[1, 1] ); +} + +//------------------------------------------------------------------------------ +function InventoryScreen::addLine( %this, %tag, %lineNum, %type, %count ) +{ + $Hud[%tag].count = %count; + + // Add label: + %yOffset = ( %lineNum * 30 ) + 28; + $Hud[%tag].data[%lineNum, 0] = new GuiTextCtrl() + { + profile = "ShellTextRightProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "228 " @ %yOffset; + extent = "80 22"; + minExtent = "8 8"; + visible = "1"; + setFirstResponder = "0"; + modal = "1"; + helpTag = "0"; + text = ""; + }; + + // Add drop menu: + $Hud[%tag].data[%lineNum, 1] = new ShellPopupMenu(INV_Menu) + { + profile = "ShellPopupProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "305 " @ %yOffset - 9; + extent = "180 36"; + minExtent = "8 8"; + visible = "1"; + setFirstResponder = "0"; + modal = "1"; + helpTag = "0"; + maxPopupHeight = "200"; + text = ""; + type = %type; + }; + + return 2; +} + +//------------------------------------------------------------------------------ +function InventoryScreen::updateHud( %this, %client, %tag ) +{ + %noSniperRifle = true; + %armor = getArmorDatablock( %client, $NameToInv[%client.favorites[0]] ); + if ( %client.lastArmor !$= %armor ) + { + %client.lastArmor = %armor; + for ( %x = 0; %x < %client.lastNumFavs; %x++ ) + messageClient( %client, 'RemoveLineHud', "", 'inventoryScreen', %x ); + %setLastNum = true; + } + + %cmt = $CurrentMissionType; +//Create - ARMOR - List + %armorList = %client.favorites[0]; + for ( %y = 0; $InvArmor[%y] !$= ""; %y++ ) + if ( $InvArmor[%y] !$= %client.favorites[0] ) + %armorList = %armorList TAB $InvArmor[%y]; + +//Create - WEAPON - List + for ( %y = 0; $InvWeapon[%y] !$= ""; %y++ ) + { + %notFound = true; + for ( %i = 0; %i < getFieldCount( %client.weaponIndex ); %i++ ) + { + %WInv = $NameToInv[$InvWeapon[%y]]; + if ( ( $InvWeapon[%y] $= %client.favorites[getField( %client.weaponIndex,%i )] ) || !%armor.max[%WInv] ) + { + %notFound = false; + break; + } + 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"; + } + } + + if ( !($InvBanList[%cmt, %WInv]) ) + { + if ( %notFound && %weaponList $= "" ) + %weaponList = $InvWeapon[%y]; + else if ( %notFound ) + %weaponList = %weaponList TAB $InvWeapon[%y]; + } + } + +//Create - PACK - List + if ( %noSniperRifle ) + { + if ( getFieldCount( %client.packIndex ) ) + %packList = %client.favorites[getField( %client.packIndex, 0 )]; + else + { + %packList = "EMPTY"; + %client.numFavs++; + } + for ( %y = 0; $InvPack[%y] !$= ""; %y++ ) + { + %PInv = $NameToInv[$InvPack[%y]]; + if ( ( $InvPack[%y] !$= %client.favorites[getField( %client.packIndex, 0 )]) && + %armor.max[%PInv] && !($InvBanList[%cmt, %PInv])) + %packList = %packList TAB $Invpack[%y]; + } + } +//Create - GRENADE - List + for ( %y = 0; $InvGrenade[%y] !$= ""; %y++ ) + { + %notFound = true; + for(%i = 0; %i < getFieldCount( %client.grenadeIndex ); %i++) + { + %GInv = $NameToInv[$InvGrenade[%y]]; + if ( ( $InvGrenade[%y] $= %client.favorites[getField( %client.grenadeIndex, %i )] ) || !%armor.max[%GInv] ) + { + %notFound = false; + break; + } + } + if ( !($InvBanList[%cmt, %GInv]) ) + { + if ( %notFound && %grenadeList $= "" ) + %grenadeList = $InvGrenade[%y]; + else if ( %notFound ) + %grenadeList = %grenadeList TAB $InvGrenade[%y]; + } + } + +//Create - MINE - List + for ( %y = 0; $InvMine[%y] !$= "" ; %y++ ) + { + %notFound = true; + // ----------------------------------------------------------------------------------------------------- + // z0dd - ZOD, 4/24/02. This was broken, Fixed. + for(%i = 0; %i < getFieldCount( %client.mineIndex ); %i++) + { + %MInv = $NameToInv[$InvMine[%y]]; + if ( ( $InvMine[%y] $= %client.favorites[getField( %client.mineIndex, %i )] ) || !%armor.max[%MInv] ) + { + %notFound = false; + break; + } + } + // ----------------------------------------------------------------------------------------------------- + if ( !($InvBanList[%cmt, %MInv]) ) + { + if ( %notFound && %mineList $= "" ) + %mineList = $InvMine[%y]; + else if ( %notFound ) + %mineList = %mineList TAB $InvMine[%y]; + } + } + %client.numFavsCount++; + messageClient( %client, 'SetLineHud', "", %tag, 0, "Armor:", %armorList, armor, %client.numFavsCount ); + %lineCount = 1; + + for ( %x = 0; %x < %armor.maxWeapons; %x++ ) + { + %client.numFavsCount++; + if ( %x < getFieldCount( %client.weaponIndex ) ) + { + %list = %client.favorites[getField( %client.weaponIndex,%x )]; + if ( %list $= Invalid ) + { + %client.favorites[%client.numFavs] = "INVALID"; + %client.weaponIndex = %client.weaponIndex TAB %client.numFavs; + } + } + else + { + %list = "EMPTY"; + %client.favorites[%client.numFavs] = "EMPTY"; + %client.weaponIndex = %client.weaponIndex TAB %client.numFavs; + %client.numFavs++; + } + if ( %list $= empty ) + %list = %list TAB %weaponList; + else + %list = %list TAB %weaponList TAB "EMPTY"; + messageClient( %client, 'SetLineHud', "", %tag, %x + %lineCount, "Weapon Slot " @ %x + 1 @ ": ", %list , weapon, %client.numFavsCount ); + } + %lineCount = %lineCount + %armor.maxWeapons; + + %client.numFavsCount++; + if ( getField( %packList, 0 ) !$= empty && %noSniperRifle ) + %packList = %packList TAB "EMPTY"; + %packText = %packList; + %packOverFlow = ""; + if ( strlen( %packList ) > 255 ) + { + %packText = getSubStr( %packList, 0, 255 ); + %packOverFlow = getSubStr( %packList, 255, 512 ); + } + messageClient( %client, 'SetLineHud', "", %tag, %lineCount, "Pack:", %packText, pack, %client.numFavsCount, %packOverFlow ); + %lineCount++; + + for( %x = 0; %x < %armor.maxGrenades; %x++ ) + { + %client.numFavsCount++; + if ( %x < getFieldCount( %client.grenadeIndex ) ) + { + %list = %client.favorites[getField( %client.grenadeIndex, %x )]; + if (%list $= Invalid) + { + %client.favorites[%client.numFavs] = "INVALID"; + %client.grenadeIndex = %client.grenadeIndex TAB %client.numFavs; + } + } + else + { + %list = "EMPTY"; + %client.favorites[%client.numFavs] = "EMPTY"; + %client.grenadeIndex = %client.grenadeIndex TAB %client.numFavs; + %client.numFavs++; + } + + if ( %list $= empty ) + %list = %list TAB %grenadeList; + else + %list = %list TAB %grenadeList TAB "EMPTY"; + + messageClient( %client, 'SetLineHud', "", %tag, %x + %lineCount, "Grenade:", %list, grenade, %client.numFavsCount ); + } + %lineCount = %lineCount + %armor.maxGrenades; + + for ( %x = 0; %x < %armor.maxMines; %x++ ) + { + %client.numFavsCount++; + if ( %x < getFieldCount( %client.mineIndex ) ) + { + %list = %client.favorites[getField( %client.mineIndex, %x )]; + if ( %list $= Invalid ) + { + %client.favorites[%client.numFavs] = "INVALID"; + %client.mineIndex = %client.mineIndex TAB %client.numFavs; + } + } + else + { + %list = "EMPTY"; + %client.favorites[%client.numFavs] = "EMPTY"; + %client.mineIndex = %client.mineIndex TAB %client.numFavs; + %client.numFavs++; + } + + if ( %list !$= Invalid ) + { + if ( %list $= empty ) + %list = %list TAB %mineList; + else if ( %mineList !$= "" ) + %list = %list TAB %mineList TAB "EMPTY"; + else + %list = %list TAB "EMPTY"; + } + + messageClient( %client, 'SetLineHud', "", %tag, %x + %lineCount, "Mine:", %list, mine, %client.numFavsCount ); + } + + if ( %setLastNum ) + %client.lastNumFavs = %client.numFavs; +} + +//------------------------------------------------------------------------------ +function buyFavorites(%client) +{ + if(isObject(Game)) // z0dd - ZOD, 8/9/03. No armors in Spawn CTF. + { + if(Game.class $= SCtFGame) + { + buyDeployableFavorites(%client); + return; + } + } + // z0dd - ZOD, 5/27/03. Check to see if we reached the cap on armors, if so, buy ammo and go away mad. + if(%client.favorites[0] !$= "Scout" && !$Host::TournamentMode && $LimitArmors) + { + if($TeamArmorCount[%client.team, $NameToInv[%client.favorites[0]]] >= $TeamArmorMax) + { + messageClient(%client, 'MsgTeamDepObjCount', '\c2Your team has reached the maximum (%2) allotment of %1 armors', %client.favorites[0], $TeamArmorMax); + getAmmoStationLovin(%client); + return; + } + } + + // z0dd - ZOD, 5/27/03. Increase the teams armor count and let the player know whats left etc. + if(!$Host::TournamentMode && $LimitArmors) + { + $TeamArmorCount[%client.team, %client.armor]--; + $TeamArmorCount[%client.team, $NameToInv[%client.favorites[0]]]++; + if(%client.favorites[0] !$= "Scout") + messageClient(%client, 'MsgTeamDepObjCount', '\c2Your team has %1 of %2 %3 armors in use', $TeamArmorCount[%client.team, $NameToInv[%client.favorites[0]]], $TeamArmorMax, %client.favorites[0]); + } + + // don't forget -- for many functions, anything done here also needs to be done + // below in buyDeployableFavorites !!! + %client.player.clearInventory(); + %client.setWeaponsHudClearAll(); + %cmt = $CurrentMissionType; + + %curArmor = %client.player.getDatablock(); + %curDmgPct = getDamagePercent(%curArmor.maxDamage, %client.player.getDamageLevel()); + + // armor + %client.armor = $NameToInv[%client.favorites[0]]; + %client.player.setArmor( %client.armor ); + %newArmor = %client.player.getDataBlock(); + + %client.player.setDamageLevel(%curDmgPct * %newArmor.maxDamage); + %weaponCount = 0; + + // weapons + for(%i = 0; %i < getFieldCount( %client.weaponIndex ); %i++) + { + %inv = $NameToInv[%client.favorites[getField( %client.weaponIndex, %i )]]; + + if( %inv !$= "" ) + { + %weaponCount++; + %client.player.setInventory( %inv, 1 ); + } + + // ---------------------------------------------------- + // z0dd - ZOD, 4/24/02. Code optimization. + if ( %inv.image.ammo !$= "" ) + %client.player.setInventory( %inv.image.ammo, 999 ); + // ---------------------------------------------------- + } + %client.player.weaponCount = %weaponCount; + + // pack + %pCh = $NameToInv[%client.favorites[%client.packIndex]]; + if ( %pCh $= "" ) + %client.clearBackpackIcon(); + else + %client.player.setInventory( %pCh, 1 ); + + // if this pack is a deployable that has a team limit, warn the purchaser + // if it's a deployable turret, the limit depends on the number of players (deployables.cs) + if(%pCh $= "TurretIndoorDeployable" || %pCh $= "TurretOutdoorDeployable") + %maxDep = countTurretsAllowed(%pCh); + else + %maxDep = $TeamDeployableMax[%pCh]; + + if(%maxDep !$= "") + { + %depSoFar = $TeamDeployedCount[%client.player.team, %pCh]; + %packName = %client.favorites[%client.packIndex]; + + if(Game.numTeams > 1) + %msTxt = "Your team has "@%depSoFar@" of "@%maxDep SPC %packName@"s deployed."; + else + %msTxt = "You have deployed "@%depSoFar@" of "@%maxDep SPC %packName@"s."; + + messageClient(%client, 'MsgTeamDepObjCount', %msTxt); + } + + // grenades + for ( %i = 0; %i < getFieldCount( %client.grenadeIndex ); %i++ ) + { + if ( !($InvBanList[%cmt, $NameToInv[%client.favorites[getField( %client.grenadeIndex, %i )]]]) ) + %client.player.setInventory( $NameToInv[%client.favorites[getField( %client.grenadeIndex,%i )]], 30 ); + } + + %client.player.lastGrenade = $NameToInv[%client.favorites[getField( %client.grenadeIndex,%i )]]; + + // if player is buying cameras, show how many are already deployed + if(%client.favorites[%client.grenadeIndex] $= "Deployable Camera") + { + %maxDep = $TeamDeployableMax[DeployedCamera]; + %depSoFar = $TeamDeployedCount[%client.player.team, DeployedCamera]; + if(Game.numTeams > 1) + %msTxt = "Your team has "@%depSoFar@" of "@%maxDep@" Deployable Cameras placed."; + else + %msTxt = "You have placed "@%depSoFar@" of "@%maxDep@" Deployable Cameras."; + messageClient(%client, 'MsgTeamDepObjCount', %msTxt); + } + + // mines + // ----------------------------------------------------------------------------------------------------- + // z0dd - ZOD, 4/24/02. Old code did not check to see if mines are banned, fixed. + for ( %i = 0; %i < getFieldCount( %client.mineIndex ); %i++ ) + { + if ( !($InvBanList[%cmt, $NameToInv[%client.favorites[getField( %client.mineIndex, %i )]]]) ) + %client.player.setInventory( $NameToInv[%client.favorites[getField( %client.mineIndex,%i )]], 30 ); + } + // ----------------------------------------------------------------------------------------------------- + // miscellaneous stuff -- Repair Kit, Beacons, Targeting Laser + if ( !($InvBanList[%cmt, RepairKit]) ) + %client.player.setInventory( RepairKit, 1 ); + if ( !($InvBanList[%cmt, Beacon]) ) + %client.player.setInventory( Beacon, 20 ); // z0dd - ZOD, 4/24/02. 400 was a bit much, changed to 20 + if ( !($InvBanList[%cmt, TargetingLaser]) ) + %client.player.setInventory( TargetingLaser, 1 ); + + // ammo pack pass -- hack! hack! + if( %pCh $= "AmmoPack" ) + invAmmoPackPass(%client); +} + +//------------------------------------------------------------------------------ +function buyDeployableFavorites(%client) +{ + %player = %client.player; + %prevPack = %player.getMountedImage($BackpackSlot); + %player.clearInventory(); + %client.setWeaponsHudClearAll(); + %cmt = $CurrentMissionType; + + // players cannot buy armor from deployable inventory stations + %weapCount = 0; + for ( %i = 0; %i < getFieldCount( %client.weaponIndex ); %i++ ) + { + %inv = $NameToInv[%client.favorites[getField( %client.weaponIndex, %i )]]; + if ( !($InvBanList[DeployInv, %inv]) ) + { + %player.setInventory( %inv, 1 ); + // increment weapon count if current armor can hold this weapon + if(%player.getDatablock().max[%inv] > 0) + %weapCount++; + // --------------------------------------------- + // z0dd - ZOD, 4/24/02. Code streamlining. + if ( %inv.image.ammo !$= "" ) + %player.setInventory( %inv.image.ammo, 999 ); + // --------------------------------------------- + if(%weapCount >= %player.getDatablock().maxWeapons) + break; + } + } + %player.weaponCount = %weapCount; + // give player the grenades and mines they chose, beacons, and a repair kit + for ( %i = 0; %i < getFieldCount( %client.grenadeIndex ); %i++) + { + %GInv = $NameToInv[%client.favorites[getField( %client.grenadeIndex, %i )]]; + %client.player.lastGrenade = %GInv; + if ( !($InvBanList[DeployInv, %GInv]) ) + %player.setInventory( %GInv, 30 ); + } + + // if player is buying cameras, show how many are already deployed + if(%client.favorites[%client.grenadeIndex] $= "Deployable Camera") + { + %maxDep = $TeamDeployableMax[DeployedCamera]; + %depSoFar = $TeamDeployedCount[%client.player.team, DeployedCamera]; + if(Game.numTeams > 1) + %msTxt = "Your team has "@%depSoFar@" of "@%maxDep@" Deployable Cameras placed."; + else + %msTxt = "You have placed "@%depSoFar@" of "@%maxDep@" Deployable Cameras."; + messageClient(%client, 'MsgTeamDepObjCount', %msTxt); + } + + for ( %i = 0; %i < getFieldCount( %client.mineIndex ); %i++ ) + { + %MInv = $NameToInv[%client.favorites[getField( %client.mineIndex, %i )]]; + if ( !($InvBanList[DeployInv, %MInv]) ) + %player.setInventory( %MInv, 30 ); + } + if ( !($InvBanList[DeployInv, Beacon]) && !($InvBanList[%cmt, Beacon]) ) + %player.setInventory( Beacon, 20 ); // z0dd - ZOD, 4/24/02. 400 was a bit much, changed to 20. + if ( !($InvBanList[DeployInv, RepairKit]) && !($InvBanList[%cmt, RepairKit]) ) + %player.setInventory( RepairKit, 1 ); + if ( !($InvBanList[DeployInv, TargetingLaser]) && !($InvBanList[%cmt, TargetingLaser]) ) + %player.setInventory( TargetingLaser, 1 ); + + // players cannot buy deployable station packs from a deployable inventory station + %packChoice = $NameToInv[%client.favorites[%client.packIndex]]; + if ( !($InvBanList[DeployInv, %packChoice]) ) + %player.setInventory( %packChoice, 1 ); + + // if this pack is a deployable that has a team limit, warn the purchaser + // if it's a deployable turret, the limit depends on the number of players (deployables.cs) + if(%packChoice $= "TurretIndoorDeployable" || %packChoice $= "TurretOutdoorDeployable") + %maxDep = countTurretsAllowed(%packChoice); + else + %maxDep = $TeamDeployableMax[%packChoice]; + if((%maxDep !$= "") && (%packChoice !$= "InventoryDeployable")) + { + %depSoFar = $TeamDeployedCount[%client.player.team, %packChoice]; + %packName = %client.favorites[%client.packIndex]; + + if(Game.numTeams > 1) + %msTxt = "Your team has "@%depSoFar@" of "@%maxDep SPC %packName@"s deployed."; + else + %msTxt = "You have deployed "@%depSoFar@" of "@%maxDep SPC %packName@"s."; + + messageClient(%client, 'MsgTeamDepObjCount', %msTxt); + } + + if(%prevPack > 0) + { + // if player had a "forbidden" pack (such as a deployable inventory station) + // BEFORE visiting a deployed inventory station AND still has that pack chosen + // as a favorite, give it back + if((%packChoice $= %prevPack.item) && ($InvBanList[DeployInv, %packChoice])) + %player.setInventory( %prevPack.item, 1 ); + } + + if(%packChoice $= "AmmoPack") + invAmmoPackPass(%client); +} + +//------------------------------------------------------------------------------------- +function getAmmoStationLovin(%client) +{ + // z0dd - ZOD, 4/24/02. This function was quite a mess, needed rewrite + %cmt = $CurrentMissionType; + + // weapons + for(%i = 0; %i < %client.player.weaponSlotCount; %i++) + { + %weapon = %client.player.weaponSlot[%i]; + if ( %weapon.image.ammo !$= "" ) + %client.player.setInventory( %weapon.image.ammo, 999 ); + } + + // grenades + for(%i = 0; $InvGrenade[%i] !$= ""; %i++) // z0dd - ZOD, 5/27/03. Clear them all in one pass + %player.setInventory($NameToInv[$InvGrenade[%i]], 0); + + for ( %i = 0; %i < getFieldCount( %client.grenadeIndex ); %i++ ) + { + %client.player.lastGrenade = $NameToInv[%client.favorites[getField( %client.grenadeIndex, %i )]]; + } + %grenType = %client.player.lastGrenade; + if(%grenType $= "") + { + %grenType = Grenade; + } + if ( !($InvBanList[%cmt, %grenType]) ) + %client.player.setInventory( %grenType, 30 ); + + if(%grenType $= "Deployable Camera") + { + %maxDep = $TeamDeployableMax[DeployedCamera]; + %depSoFar = $TeamDeployedCount[%client.player.team, DeployedCamera]; + if(Game.numTeams > 1) + %msTxt = "Your team has "@%depSoFar@" of "@%maxDep@" Deployable Cameras placed."; + else + %msTxt = "You have placed "@%depSoFar@" of "@%maxDep@" Deployable Cameras."; + messageClient(%client, 'MsgTeamDepObjCount', %msTxt); + } + + // Mines + for(%i = 0; $InvMine[%i] !$= ""; %i++) // z0dd - ZOD, 5/27/03. Clear them all in one pass + %player.setInventory($NameToInv[$InvMine[%i]], 0); + + for ( %i = 0; %i < getFieldCount( %client.mineIndex ); %i++ ) + { + %client.player.lastMine = $NameToInv[%client.favorites[getField( %client.mineIndex, %i )]]; + } + %mineType = %client.player.lastMine; + if(%mineType $= "") + { + %mineType = Mine; + } + if ( !($InvBanList[%cmt, %mineType]) ) + %client.player.setInventory( %mineType, 30 ); + + // miscellaneous stuff -- Repair Kit, Beacons, Targeting Laser + if ( !($InvBanList[%cmt, RepairKit]) ) + %client.player.setInventory( RepairKit, 1 ); + + if ( !($InvBanList[%cmt, Beacon]) ) + %client.player.setInventory( Beacon, 20 ); + + if ( !($InvBanList[%cmt, TargetingLaser]) ) + %client.player.setInventory( TargetingLaser, 1 ); + + if( %client.player.getMountedImage($BackpackSlot) $= "AmmoPack" ) + invAmmoPackPass(%client); +} + +function invAmmoPackPass(%client) +{ + // "normal" ammo stuff (everything but mines and grenades) + for ( %idx = 0; %idx < $numAmmoItems; %idx++ ) + { + %ammo = $AmmoItem[%idx]; + %client.player.incInventory(%ammo, AmmoPack.max[%ammo]); + } + //our good friends, the grenade family *SIGH* + // first find out what type of grenade the player has selected + %grenFav = %client.favorites[getField(%client.grenadeIndex, 0)]; + if((%grenFav !$= "EMPTY") && (%grenFav !$= "INVALID")) + %client.player.incInventory($NameToInv[%grenFav], AmmoPack.max[$NameToInv[%grenFav]]); + // now the same check for mines + %mineFav = %client.favorites[getField(%client.mineIndex, 0)]; + if((%mineFav !$= "EMPTY") && (%mineFav !$= "INVALID") && !($InvBanList[%cmt, Mine])) + %client.player.incInventory($NameToInv[%mineFav], AmmoPack.max[$NameToInv[%mineFav]]); +} + +//------------------------------------------------------------------------------ +function loadFavorite( %index, %echo ) +{ + $pref::FavCurrentSelect = %index; + %list = mFloor( %index / 10 ); + + if ( isObject( $Hud['inventoryScreen'] ) ) + { + // Deselect the old tab: + if ( InventoryScreen.selId !$= "" ) + $Hud['inventoryScreen'].staticData[0, InventoryScreen.selId].setValue( false ); + + // Make sure we are looking at the same list: + if ( $pref::FavCurrentList != %list ) + { + %favListStart = %list * 10; + %text = "Favorites " @ %favListStart + 1 SPC "-" SPC %favListStart + 10; + $Hud['inventoryScreen'].staticData[0, 0].onSelect( %list, %text, true ); + } + + // Select the new tab: + %tab = $pref::FavCurrentSelect - ( $pref::FavCurrentList * 10 ) + 1; + InventoryScreen.selId = %tab; + $Hud['inventoryScreen'].staticData[0, %tab].setValue( true ); + + // Update the Edit Name field: + $Hud['inventoryScreen'].staticData[1, 1].setValue( $pref::FavNames[%index] ); + } + + if ( %echo ) + addMessageHudLine( "Inventory set \"" @ $pref::FavNames[%index] @ "\" selected." ); + + commandToServer( 'setClientFav', $pref::Favorite[%index] ); +} + +//------------------------------------------------------------------------------ +function saveFavorite() +{ + if ( $pref::FavCurrentSelect !$= "" ) + { + %favName = $Hud['inventoryScreen'].staticData[1, 1].getValue(); + $pref::FavNames[$pref::FavCurrentSelect] = %favName; + $Hud['inventoryScreen'].staticData[0, $pref::FavCurrentSelect - ($pref::FavCurrentList * 10) + 1].setText( strupr( %favName ) ); + //$Hud[%tag].staticData[1, 1].setValue( %favName ); + %favList = $Hud['inventoryScreen'].data[0, 1].type TAB $Hud['inventoryScreen'].data[0, 1].getValue(); + for ( %i = 1; %i < $Hud['inventoryScreen'].count; %i++ ) + { + %name = $Hud['inventoryScreen'].data[%i, 1].getValue(); + if ( %name $= invalid ) + %name = "EMPTY"; + %favList = %favList TAB $Hud['inventoryScreen'].data[%i, 1].type TAB %name; + } + $pref::Favorite[$pref::FavCurrentSelect] = %favList; + echo("exporting pref::* to ClientPrefs.cs"); + export("$pref::*", "prefs/ClientPrefs.cs", False); + } +// else +// addMessageHudLine("Must First Select A Favorite Button."); +} + +//------------------------------------------------------------------------------ +function addQuickPackFavorite( %pack, %item ) +{ + // this has been such a success it has been changed to handle grenades + // and other equipment as well as packs so everything seems to be called 'pack' + // including the function itself. The default IS pack + + if(%item $= "") + %item = "Pack"; + %packFailMsg = "You cannot use that equipment with your selected loadout."; + if ( !isObject($Hud['inventoryScreen'].staticData[1, 1]) || $Hud['inventoryScreen'].staticData[1, 1].getValue() $= "" ) + { + //if the player hasnt brought up the inv screen we use his current fav + %currentFav = $pref::Favorite[$pref::FavCurrentSelect]; + //echo(%currentFav); + + for ( %i = 0; %i < getFieldCount( %currentFav ); %i++ ) + { + %type = getField( %currentFav, %i ); + %equipment = getField( %currentFav, %i++ ); + + %invalidPack = checkPackValidity(%pack, %equipment, %item ); + if(%invalidPack) + { + addMessageHudLine( %packFailMsg ); + return; + + } + // Success-------------------------------------------------- + if ( %type $= %item ) + %favList = %favList @ %type TAB %pack @ "\t"; + else + %favList = %favList @ %type TAB %equipment @ "\t"; + } + //echo(%favList); + } + else + { + //otherwise we go with whats on the invScreen (even if its asleep) + %armor = $Hud['inventoryScreen'].data[0, 1].getValue(); + + // check pack validity with armor + %invalidPack = checkPackValidity(%pack, %armor, %item ); + if(%invalidPack) + { + addMessageHudLine( %packFailMsg ); + return; + + } + %favList = $Hud['inventoryScreen'].data[0, 1].type TAB %armor; + for ( %i = 1; %i < $Hud['inventoryScreen'].count; %i++ ) + { + //echo( $Hud['inventoryScreen'].Data[%i, 1].type); + %type = $Hud['inventoryScreen'].data[%i, 1].type; + %equipment = $Hud['inventoryScreen'].data[%i, 1].getValue(); + + if(%type $= %item) + %equipment = %pack; + + // Special Cases again------------------------------------------------ + %invalidPack = checkPackValidity(%pack, %equipment, %item ); + if(%invalidPack) + { + addMessageHudLine( %packFailMsg ); + return; + + } + + %favList = %favList TAB %type TAB %equipment; + } + //echo(%favList); + } + commandToServer( 'setClientFav', %favList ); + + //we message the player real nice like + addMessageHudLine( "Inventory updated to " @ %pack @ "." ); +} + +function checkPackValidity(%pack, %equipment, %item) +{ + //echo("validityChecking:" SPC %pack SPC %equipment); + + // this is mostly for ease of mod makers + // this is the base restrictions stuff + // for your mod just overwrite this function and + // change the restrictions and onlyUses + + // you must have #1 to use #2 + //%restrict[#1, #2] = true; + + %restrict["Scout", "Inventory Station"] = true; + %restrict["Scout", "Landspike Turret"] = true; + %restrict["Scout", "Spider Clamp Turret"] = true; + %restrict["Scout", "ELF Turret Barrel"] = true; + %restrict["Scout", "Mortar Turret Barrel"] = true; + %restrict["Scout", "AA Turret Barrel"] = true; + %restrict["Scout", "Plasma Turret Barrel"] = true; + %restrict["Scout", "Missile Turret Barrel"] = true; + %restrict["Assault", "Cloak Pack"] = true; + %restrict["Juggernaut", "Cloak Pack"] = true; + + // you can only use #1 if you have a #2 of type #3 + //%require[#1] = #2 TAB #3; + + %require["Laser Rifle"] = "Pack" TAB "Energy Pack"; + + + if(%restrict[%equipment, %pack] ) + return true; + + else if(%require[%equipment] !$="" ) + { + if(%item $= getField(%require[%equipment], 0) ) + { + if(%pack !$= getField(%require[%equipment], 1) ) + return true; + } + } +} + + +//------------------------------------------------------------------------------ +function setDefaultInventory(%client) +{ + commandToClient(%client,'InitLoadClientFavorites'); +} + +//------------------------------------------------------------------------------ +function checkInventory( %client, %text ) +{ + %armor = getArmorDatablock( %client, $NameToInv[getField( %text, 1 )] ); + %list = getField( %text, 0 ) TAB getField( %text, 1 ); + %cmt = $CurrentMissionType; + for( %i = 3; %i < getFieldCount( %text ); %i = %i + 2 ) + { + %inv = $NameToInv[getField(%text,%i)]; + if ( (( %armor.max[%inv] && !($InvBanList[%cmt, %inv]) ) || + getField( %text, %i ) $= Empty || getField( %text, %i ) $= Invalid) + && (($InvTotalCount[getField( %text, %i - 1 )] - $BanCount[getField( %text, %i - 1 )]) > 0)) + %list = %list TAB getField( %text, %i - 1 ) TAB getField( %text, %i ); + else if( $InvBanList[%cmt, %inv] || %inv $= empty || %inv $= "") + %list = %list TAB getField( %text, %i - 1 ) TAB "INVALID"; + } + return %list; +} + +//------------------------------------------------------------------------------ +function getArmorDatablock(%client, %size) +{ + if ( %client.race $= "Bioderm" ) + %armor = %size @ "Male" @ %client.race @ Armor; + else + %armor = %size @ %client.sex @ %client.race @ Armor; + return %armor; +} + +//------------------------------------------------------------------------------ +function InventoryScreen::onWake(%this) +{ + if ( $HudHandle[inventoryScreen] !$= "" ) + alxStop( $HudHandle[inventoryScreen] ); + alxPlay(HudInventoryActivateSound, 0, 0, 0); + $HudHandle[inventoryScreen] = alxPlay(HudInventoryHumSound, 0, 0, 0); + + if ( isObject( hudMap ) ) + { + hudMap.pop(); + hudMap.delete(); + } + new ActionMap( hudMap ); + hudMap.blockBind( moveMap, toggleScoreScreen ); + hudMap.blockBind( moveMap, toggleCommanderMap ); + hudMap.bindCmd( keyboard, escape, "", "InventoryScreen.onDone();" ); + hudMap.push(); +} + +//------------------------------------------------------------------------------ +function InventoryScreen::onSleep() +{ + hudMap.pop(); + hudMap.delete(); + alxStop($HudHandle[inventoryScreen]); + alxPlay(HudInventoryDeactivateSound, 0, 0, 0); + $HudHandle[inventoryScreen] = ""; +} + +//------------------------------------------------------------------------------ +function InventoryScreen::onDone( %this ) +{ + toggleCursorHuds( 'inventoryScreen' ); +} + +//------------------------------------------------------------------------------ +function InventoryScreen::onTabSelect( %this, %favId ) +{ + loadFavorite( %favId, 0 ); +} + +function createInvBanCount() +{ + $BanCount["Armor"] = 0; + $BanCount["Weapon"] = 0; + $BanCount["Pack"] = 0; + $BanCount["Grenade"] = 0; + $BanCount["Mine"] = 0; + + for(%i = 0; $InvArmor[%i] !$= ""; %i++) + if($InvBanList[$CurrentMissionType, $NameToInv[$InvArmor[%i]]]) + $BanCount["Armor"]++; + $InvTotalCount["Armor"] = %i; + + for(%i = 0; $InvWeapon[%i] !$= ""; %i++) + if($InvBanList[$CurrentMissionType, $NameToInv[$InvWeapon[%i]]]) + $BanCount["Weapon"]++; + $InvTotalCount["Weapon"] = %i; + + for(%i = 0; $InvPack[%i] !$= ""; %i++) + if($InvBanList[$CurrentMissionType, $NameToInv[$InvPack[%i]]]) + $BanCount["Pack"]++; + $InvTotalCount["Pack"] = %i; + + for(%i = 0; $InvGrenade[%i] !$= ""; %i++) + if($InvBanList[$CurrentMissionType, $NameToInv[$InvGrenade[%i]]]) + $BanCount["Grenade"]++; + $InvTotalCount["Grenade"] = %i; + + for(%i = 0; $InvMine[%i] !$= ""; %i++) + if($InvBanList[$CurrentMissionType, $NameToInv[$InvMine[%i]]]) + $BanCount["Mine"]++; + $InvTotalCount["Mine"] = %i; +} + +// z0dd - ZOD, 5/17/03. New functions, limit armor types you can purchase +function countArmorAllowed() +{ + // This function is called from DefaultGame::assignClientTeam + // and loadMissionStage2, so its not run constantly. + + for(%j = 1; %j < Game.numTeams; %j++) + %teamPlayerCount[%j] = 0; + + %numClients = ClientGroup.getCount(); + for(%i = 0; %i < %numClients; %i++) + { + %cl = ClientGroup.getObject(%i); + if(%cl.team > 0) + %teamPlayerCount[%cl.team]++; + } + // the bigger team determines the number of armors allowed + %maxPlayers = %teamPlayerCount[1] > %teamPlayerCount[2] ? %teamPlayerCount[1] : %teamPlayerCount[2]; + if(%maxPlayers >= 16) + %teamArmorMax = mFloor(%maxPlayers * 0.38); + else + %teamArmorMax = 6; + + return $TeamArmorMax = %teamArmorMax; +} + +function resetArmorMaxes() +{ + for(%i = 0; %i <= Game.numTeams; %i++) + { + $TeamArmorCount[%i, Light] = 0; + $TeamArmorCount[%i, Medium] = 0; + $TeamArmorCount[%i, Heavy] = 0; + } +} diff --git a/packs/sensorjammerpack.cs b/packs/sensorjammerpack.cs new file mode 100644 index 0000000..a35a4c6 --- /dev/null +++ b/packs/sensorjammerpack.cs @@ -0,0 +1,147 @@ +// ------------------------------------------------------------------ +// SENSOR JAMMER PACK +// +// When activated, the sensor jammer pack emits a sensor-jamming field of +// 20m radius. Any players within this field are completely invisible to +// all sensors, turrets and cameras. +// +// When not activated, the pack has no effect. +// +datablock EffectProfile(SensorJammerPackActivateEffect) +{ + effectname = "packs/cloak_on"; + minDistance = 2.5; + maxDistance = 2.5; +}; + +datablock AudioProfile(SensorJammerActivateSound) +{ + filename = "fx/packs/sensorjammerpack_on.wav"; + description = ClosestLooping3d; + preload = true; +}; + +datablock ShapeBaseImageData(SensorJammerPackImage) +{ + shapeFile = "pack_upgrade_sensorjammer.dts"; + item = SensorJammerPack; + mountPoint = 1; + offset = "0 0 0"; + + usesEnergy = true; + minEnergy = 3; + + stateName[0] = "Idle"; + stateTransitionOnTriggerDown[0] = "Activate"; + + stateName[1] = "Activate"; + stateScript[1] = "onActivate"; + stateSequence[1] = "fire"; + stateSound[1] = SensorJammerActivateSound; + stateEnergyDrain[1] = 10.5; + stateTransitionOnTriggerUp[1] = "Deactivate"; + stateTransitionOnNoAmmo[1] = "Deactivate"; + + stateName[2] = "Deactivate"; + stateScript[2] = "onDeactivate"; + stateTransitionOnTimeout[2] = "Idle"; +}; + +datablock ItemData(SensorJammerPack) +{ + className = Pack; + catagory = "Packs"; + shapeFile = "pack_upgrade_sensorjammer.dts"; + mass = 1; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 2; + rotate = true; + image = "SensorJammerPackImage"; + pickUpName = "a sensor jammer pack"; + + computeCRC = true; +}; + + +datablock SensorData(JammerSensorObjectPassive) //v2 was commented out... +{ + // same detection info as 'PlayerObject' sensorData + detects = true; + detectsUsingLOS = true; + detectsPassiveJammed = true; + detectRadius = 2000; + detectionPings = false; + detectsFOVOnly = true; + detectFOVPercent = 1.3; + useObjectFOV = true; + + //detectscloaked = 1; //v2 + + jams = true; + jamsOnlyGroup = true; + jamsUsingLOS = true; + jamRadius = 0; +}; + +datablock SensorData(JammerSensorObjectActive) +{ + // same detection info as 'PlayerObject' sensorData + detects = true; + detectsUsingLOS = true; + detectsPassiveJammed = true; + detectRadius = 2000; + detectionPings = false; + detectsFOVOnly = true; + detectFOVPercent = 1.3; + useObjectFOV = true; + + detectscloaked = 1; //v2 + + jams = true; + jamsOnlyGroup = true; + jamsUsingLOS = true; + jamRadius = 30; +}; + +function SensorJammerPackImage::onMount(%data, %obj, %slot) +{ + setTargetSensorData(%obj.client.target, JammerSensorObjectPassive); //v2 + %obj.setImageTrigger(%slot, false); + commandToClient( %obj.client, 'setSenJamIconOff' ); + //%obj.setJammerFX(false); +} + +function deactivateJammer(%data, %obj, %slot) +{ + SensorJammerPackImage::onDeactivate(%data, %obj, %slot); +} + +function SensorJammerPackImage::onUnmount(%data, %obj, %slot) //v2 +{ + %obj.setImageTrigger(%slot, false); + setTargetSensorData(%obj.client.target, PlayerSensor); +} + +function SensorJammerPackImage::onActivate(%data, %obj, %slot) +{ + messageClient(%obj.client, 'MsgSensorJammerPackOn', '\c2Sensor jammer pack on.'); + setTargetSensorData(%obj.client.target, JammerSensorObjectActive); + commandToClient( %obj.client, 'setSenJamIconOn' ); + //%obj.setJammerFX( true ); +} + +function SensorJammerPackImage::onDeactivate(%data, %obj, %slot) +{ + messageClient(%obj.client, 'MsgSensorJammerPackOff', '\c2Sensor jammer pack off.'); + setTargetSensorData(%obj.client.target, PlayerSensor); //v2 H bug fix + %obj.setImageTrigger(%slot, false); + setTargetSensorData(%obj.client.target, JammerSensorObjectPassive); //v2 was PlayerSensor + commandToClient( %obj.client, 'setSenJamIconOff' ); + //%obj.setJammerFX( false ); +} + +function SensorJammerPack::onPickup(%this, %obj, %shape, %amount) +{ +//Nope +} diff --git a/player.cs b/player.cs new file mode 100644 index 0000000..1c6742a --- /dev/null +++ b/player.cs @@ -0,0 +1,3297 @@ +$InvincibleTime = 6; +// Load dts shapes and merge animations +exec("scripts/light_male.cs"); +exec("scripts/medium_male.cs"); +exec("scripts/heavy_male.cs"); +exec("scripts/light_female.cs"); +exec("scripts/medium_female.cs"); +exec("scripts/bioderm_light.cs"); +exec("scripts/bioderm_medium.cs"); +exec("scripts/bioderm_heavy.cs"); + +$CorpseTimeoutValue = 22 * 1000; + +//Damage Rate for entering Liquid +$DamageLava = 0.0325; +$DamageHotLava = 0.0325; +$DamageCrustyLava = 0.0325; + +$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; + +datablock SensorData(PlayerSensor) +{ + detects = true; + detectsUsingLOS = true; + detectsPassiveJammed = true; + detectRadius = 1000; + detectionPings = false; + detectsFOVOnly = true; + detectFOVPercent = 1.3; + useObjectFOV = true; +}; + +//---------------------------------------------------------------------------- + +datablock EffectProfile(ArmorJetEffect) +{ + effectname = "armor/thrust"; + minDistance = 5.0; + maxDistance = 10.0; +}; + +datablock EffectProfile(ImpactSoftEffect) +{ + effectname = "armor/light_land_soft"; + minDistance = 5.0; + maxDistance = 10.0; +}; + +datablock EffectProfile(ImpactHardEffect) +{ + effectname = "armor/light_land_hard"; + minDistance = 5.0; + maxDistance = 10.0; +}; + +datablock EffectProfile(ImpactMetalEffect) +{ + effectname = "armor/light_land_metal"; + minDistance = 5.0; + maxDistance = 10.0; +}; + +datablock EffectProfile(ImpactSnowEffect) +{ + effectname = "armor/light_land_snow"; + minDistance = 5.0; + maxDistance = 10.0; +}; + +datablock EffectProfile(CorpseLootingEffect) +{ + effectname = "weapons/generic_switch"; + minDistance = 2.5; + maxDistance = 2.5; +}; + +datablock EffectProfile(MountVehicleEffect) +{ + effectname = "vehicles/mount_dis"; + minDistance = 5; + maxDistance = 20; +}; + +datablock EffectProfile(UnmountVehicleEffect) +{ + effectname = "weapons/generic_switch"; + minDistance = 5; + maxDistance = 20; +}; + +datablock EffectProfile(GenericPainEffect) +{ + effectname = "misc/generic_pain"; + minDistance = 2.5; + maxDistance = 2.5; +}; + +datablock EffectProfile(GenericDeathEffect) +{ + effectname = "misc/generic_death"; + minDistance = 2.5; + maxDistance = 2.5; +}; + +datablock EffectProfile(ImpactHeavyWaterEasyEffect) +{ + effectname = "armor/general_water_smallsplash"; + minDistance = 5; + maxDistance = 15; +}; + +datablock EffectProfile(ImpactHeavyMediumWaterEffect) +{ + effectname = "armor/general_water_medsplash"; + minDistance = 5; + maxDistance = 15; +}; + +datablock EffectProfile(ImpactHeavyWaterHardEffect) +{ + effectname = "armor/general_water_bigsplash"; + minDistance = 5; + maxDistance = 15; +}; + +//---------------------------------------------------------------------------- +//datablock AudioProfile( DeathCrySound ) +//{ +// fileName = ""; +// description = AudioClose3d; +// preload = true; +//}; +// +//datablock AudioProfile( PainCrySound ) +//{ +// fileName = ""; +// description = AudioClose3d; +// preload = true; +//}; + +datablock AudioProfile(ArmorJetSound) +{ + filename = "fx/armor/thrust.wav"; + description = CloseLooping3d; + preload = true; + effect = ArmorJetEffect; +}; + +datablock AudioProfile(ArmorWetJetSound) +{ + filename = "fx/armor/thrust_uw.wav"; + description = CloseLooping3d; + preload = true; + effect = ArmorJetEffect; +}; + +datablock AudioProfile(MountVehicleSound) +{ + filename = "fx/vehicles/mount_dis.wav"; + description = AudioClose3d; + preload = true; + effect = MountVehicleEffect; +}; + +datablock AudioProfile(UnmountVehicleSound) +{ + filename = "fx/vehicles/mount.wav"; + description = AudioClose3d; + preload = true; + effect = UnmountVehicleEffect; +}; + +datablock AudioProfile(CorpseLootingSound) +{ + fileName = "fx/weapons/generic_switch.wav"; + description = AudioClosest3d; + preload = true; + effect = CorpseLootingEffect; +}; + +datablock AudioProfile(ArmorMoveBubblesSound) +{ + filename = "fx/armor/bubbletrail2.wav"; + description = CloseLooping3d; + preload = true; +}; + +datablock AudioProfile(WaterBreathMaleSound) +{ + filename = "fx/armor/breath_uw.wav"; + description = ClosestLooping3d; + preload = true; +}; + +datablock AudioProfile(WaterBreathFemaleSound) +{ + filename = "fx/armor/breath_fem_uw.wav"; + description = ClosestLooping3d; + preload = true; +}; + +datablock AudioProfile(waterBreathBiodermSound) +{ + filename = "fx/armor/breath_bio_uw.wav"; + description = ClosestLooping3d; + preload = true; +}; + +datablock AudioProfile(SkiAllSoftSound) +{ + filename = "fx/armor/ski_soft.wav"; + description = AudioClose3d; + preload = true; +}; + +datablock AudioProfile(SkiAllHardSound) +{ + filename = "fx/armor/ski_soft.wav"; + description = AudioClose3d; + preload = true; +}; + +datablock AudioProfile(SkiAllMetalSound) +{ + filename = "fx/armor/ski_soft.wav"; + description = AudioClose3d; + preload = true; +}; + +datablock AudioProfile(SkiAllSnowSound) +{ + filename = "fx/armor/ski_soft.wav"; + description = AudioClosest3d; + preload = true; +}; + +//SOUNDS ----- LIGHT ARMOR-------- +datablock AudioProfile(LFootLightSoftSound) +{ + filename = "fx/armor/light_LF_soft.wav"; + description = AudioClosest3d; + preload = true; +}; + +datablock AudioProfile(RFootLightSoftSound) +{ + filename = "fx/armor/light_RF_soft.wav"; + description = AudioClosest3d; + preload = true; +}; + +datablock AudioProfile(LFootLightHardSound) +{ + filename = "fx/armor/light_LF_hard.wav"; + description = AudioClose3d; + preload = true; +}; + +datablock AudioProfile(RFootLightHardSound) +{ + filename = "fx/armor/light_RF_hard.wav"; + description = AudioClose3d; + preload = true; +}; + +datablock AudioProfile(LFootLightMetalSound) +{ + filename = "fx/armor/light_LF_metal.wav"; + description = AudioClose3d; + preload = true; +}; + +datablock AudioProfile(RFootLightMetalSound) +{ + filename = "fx/armor/light_RF_metal.wav"; + description = AudioClose3d; + preload = true; +}; +datablock AudioProfile(LFootLightSnowSound) +{ + filename = "fx/armor/light_LF_snow.wav"; + description = AudioClosest3d; + preload = true; +}; + +datablock AudioProfile(RFootLightSnowSound) +{ + filename = "fx/armor/light_RF_snow.wav"; + description = AudioClosest3d; + preload = true; +}; + +datablock AudioProfile(LFootLightShallowSplashSound) +{ + filename = "fx/armor/light_LF_water.wav"; + description = AudioClose3d; + preload = true; +}; + +datablock AudioProfile(RFootLightShallowSplashSound) +{ + filename = "fx/armor/light_RF_water.wav"; + description = AudioClose3d; + preload = true; +}; + +datablock AudioProfile(LFootLightWadingSound) +{ + filename = "fx/armor/light_LF_wade.wav"; + description = AudioClose3d; + preload = true; +}; + +datablock AudioProfile(RFootLightWadingSound) +{ + filename = "fx/armor/light_RF_wade.wav"; + description = AudioClose3d; + preload = true; +}; + +datablock AudioProfile(LFootLightUnderwaterSound) +{ + filename = "fx/armor/light_LF_uw.wav"; + description = AudioClosest3d; + preload = true; +}; + +datablock AudioProfile(RFootLightUnderwaterSound) +{ + filename = "fx/armor/light_RF_uw.wav"; + description = AudioClosest3d; + preload = true; +}; + +datablock AudioProfile(LFootLightBubblesSound) +{ + filename = "fx/armor/light_LF_bubbles.wav"; + description = AudioClose3d; + preload = true; +}; + +datablock AudioProfile(RFootLightBubblesSound) +{ + filename = "fx/armor/light_RF_bubbles.wav"; + description = AudioClose3d; + preload = true; +}; + +datablock AudioProfile(ImpactLightSoftSound) +{ + filename = "fx/armor/light_land_soft.wav"; + description = AudioClose3d; + preload = true; + effect = ImpactSoftEffect; +}; + +datablock AudioProfile(ImpactLightHardSound) +{ + filename = "fx/armor/light_land_hard.wav"; + description = AudioClose3d; + preload = true; + effect = ImpactHardEffect; +}; + +datablock AudioProfile(ImpactLightMetalSound) +{ + filename = "fx/armor/light_land_metal.wav"; + description = AudioClose3d; + preload = true; + effect = ImpactMetalEffect; +}; + +datablock AudioProfile(ImpactLightSnowSound) +{ + filename = "fx/armor/light_land_snow.wav"; + description = AudioClosest3d; + preload = true; + effect = ImpactSnowEffect; +}; + +datablock AudioProfile(ImpactLightWaterEasySound) +{ + filename = "fx/armor/general_water_smallsplash2.wav"; + description = AudioClose3d; + preload = true; +}; + +datablock AudioProfile(ImpactLightWaterMediumSound) +{ + filename = "fx/armor/general_water_medsplash.wav"; + description = AudioClose3d; + preload = true; +}; + +datablock AudioProfile(ImpactLightWaterHardSound) +{ + filename = "fx/armor/general_water_bigsplash.wav"; + description = AudioDefault3d; + preload = true; +}; + +datablock AudioProfile(ExitingWaterLightSound) +{ + filename = "fx/armor/general_water_exit2.wav"; + description = AudioClose3d; + preload = true; +}; + +//SOUNDS ----- Medium ARMOR-------- +datablock AudioProfile(LFootMediumSoftSound) +{ + filename = "fx/armor/med_LF_soft.wav"; + description = AudioClosest3d; + preload = true; +}; + +datablock AudioProfile(RFootMediumSoftSound) +{ + filename = "fx/armor/med_RF_soft.wav"; + description = AudioClosest3d; + preload = true; +}; + +datablock AudioProfile(LFootMediumHardSound) +{ + filename = "fx/armor/med_LF_hard.wav"; + description = AudioClose3d; + preload = true; +}; + +datablock AudioProfile(RFootMediumHardSound) +{ + filename = "fx/armor/med_RF_hard.wav"; + description = AudioClose3d; + preload = true; +}; + +datablock AudioProfile(LFootMediumMetalSound) +{ + filename = "fx/armor/med_LF_metal.wav"; + description = AudioClose3d; + preload = true; +}; + +datablock AudioProfile(RFootMediumMetalSound) +{ + filename = "fx/armor/med_RF_metal.wav"; + description = AudioClose3d; + preload = true; +}; +datablock AudioProfile(LFootMediumSnowSound) +{ + filename = "fx/armor/med_LF_snow.wav"; + description = AudioClosest3d; + preload = true; +}; + +datablock AudioProfile(RFootMediumSnowSound) +{ + filename = "fx/armor/med_RF_snow.wav"; + description = AudioClosest3d; + preload = true; +}; + +datablock AudioProfile(LFootMediumShallowSplashSound) +{ + filename = "fx/armor/med_LF_water.wav"; + description = AudioClose3d; + preload = true; +}; + +datablock AudioProfile(RFootMediumShallowSplashSound) +{ + filename = "fx/armor/med_RF_water.wav"; + description = AudioClose3d; + preload = true; +}; + +datablock AudioProfile(LFootMediumWadingSound) +{ + filename = "fx/armor/med_LF_uw.wav"; + description = AudioClose3d; + preload = true; +}; + +datablock AudioProfile(RFootMediumWadingSound) +{ + filename = "fx/armor/med_RF_uw.wav"; + description = AudioClose3d; + preload = true; +}; + +datablock AudioProfile(LFootMediumUnderwaterSound) +{ + filename = "fx/armor/med_LF_uw.wav"; + description = AudioClosest3d; + preload = true; +}; + +datablock AudioProfile(RFootMediumUnderwaterSound) +{ + filename = "fx/armor/med_RF_uw.wav"; + description = AudioClosest3d; + preload = true; +}; + +datablock AudioProfile(LFootMediumBubblesSound) +{ + filename = "fx/armor/light_LF_bubbles.wav"; + description = AudioClose3d; + preload = true; +}; + +datablock AudioProfile(RFootMediumBubblesSound) +{ + filename = "fx/armor/light_RF_bubbles.wav"; + description = AudioClose3d; + preload = true; +}; +datablock AudioProfile(ImpactMediumSoftSound) +{ + filename = "fx/armor/med_land_soft.wav"; + description = AudioClosest3d; + preload = true; + effect = ImpactSoftEffect; +}; + +datablock AudioProfile(ImpactMediumHardSound) +{ + filename = "fx/armor/med_land_hard.wav"; + description = AudioClose3d; + preload = true; + effect = ImpactHardEffect; +}; + +datablock AudioProfile(ImpactMediumMetalSound) +{ + filename = "fx/armor/med_land_metal.wav"; + description = AudioClose3d; + preload = true; + effect = ImpactMetalEffect; +}; + +datablock AudioProfile(ImpactMediumSnowSound) +{ + filename = "fx/armor/med_land_snow.wav"; + description = AudioClosest3d; + preload = true; + effect = ImpactSnowEffect; +}; + +datablock AudioProfile(ImpactMediumWaterEasySound) +{ + filename = "fx/armor/general_water_smallsplash.wav"; + description = AudioClose3d; + preload = true; +}; + +datablock AudioProfile(ImpactMediumWaterMediumSound) +{ + filename = "fx/armor/general_water_medsplash.wav"; + description = AudioClose3d; + preload = true; +}; + +datablock AudioProfile(ImpactMediumWaterHardSound) +{ + filename = "fx/armor/general_water_bigsplash.wav"; + description = AudioDefault3d; + preload = true; +}; + +datablock AudioProfile(ExitingWaterMediumSound) +{ + filename = "fx/armor/general_water_exit.wav"; + description = AudioClose3d; + preload = true; +}; + +//SOUNDS ----- HEAVY ARMOR-------- +datablock AudioProfile(LFootHeavySoftSound) +{ + filename = "fx/armor/heavy_LF_soft.wav"; + description = AudioClosest3d; + preload = true; +}; + +datablock AudioProfile(RFootHeavySoftSound) +{ + filename = "fx/armor/heavy_RF_soft.wav"; + description = AudioClosest3d; + preload = true; +}; + +datablock AudioProfile(LFootHeavyHardSound) +{ + filename = "fx/armor/heavy_LF_hard.wav"; + description = AudioClose3d; + preload = true; +}; + +datablock AudioProfile(RFootHeavyHardSound) +{ + filename = "fx/armor/heavy_RF_hard.wav"; + description = AudioClose3d; + preload = true; +}; + +datablock AudioProfile(LFootHeavyMetalSound) +{ + filename = "fx/armor/heavy_LF_metal.wav"; + description = AudioClose3d; + preload = true; +}; + +datablock AudioProfile(RFootHeavyMetalSound) +{ + filename = "fx/armor/heavy_RF_metal.wav"; + description = AudioClose3d; + preload = true; +}; +datablock AudioProfile(LFootHeavySnowSound) +{ + filename = "fx/armor/heavy_LF_snow.wav"; + description = AudioClosest3d; + preload = true; +}; + +datablock AudioProfile(RFootHeavySnowSound) +{ + filename = "fx/armor/heavy_RF_snow.wav"; + description = AudioClosest3d; + preload = true; +}; + +datablock AudioProfile(LFootHeavyShallowSplashSound) +{ + filename = "fx/armor/heavy_LF_water.wav"; + description = AudioClose3d; + preload = true; +}; + +datablock AudioProfile(RFootHeavyShallowSplashSound) +{ + filename = "fx/armor/heavy_RF_water.wav"; + description = AudioClose3d; + preload = true; +}; + +datablock AudioProfile(LFootHeavyWadingSound) +{ + filename = "fx/armor/heavy_LF_uw.wav"; + description = AudioClose3d; + preload = true; +}; + +datablock AudioProfile(RFootHeavyWadingSound) +{ + filename = "fx/armor/heavy_RF_uw.wav"; + description = AudioClose3d; + preload = true; +}; + +datablock AudioProfile(LFootHeavyUnderwaterSound) +{ + filename = "fx/armor/heavy_LF_uw.wav"; + description = AudioClosest3d; + preload = true; +}; + +datablock AudioProfile(RFootHeavyUnderwaterSound) +{ + filename = "fx/armor/heavy_RF_uw.wav"; + description = AudioClosest3d; + preload = true; +}; + +datablock AudioProfile(LFootHeavyBubblesSound) +{ + filename = "fx/armor/light_LF_bubbles.wav"; + description = AudioClose3d; + preload = true; +}; + +datablock AudioProfile(RFootHeavyBubblesSound) +{ + filename = "fx/armor/light_RF_bubbles.wav"; + description = AudioClose3d; + preload = true; +}; +datablock AudioProfile(ImpactHeavySoftSound) +{ + filename = "fx/armor/heavy_land_soft.wav"; + description = AudioClose3d; + preload = true; + effect = ImpactSoftEffect; +}; + +datablock AudioProfile(ImpactHeavyHardSound) +{ + filename = "fx/armor/heavy_land_hard.wav"; + description = AudioClose3d; + preload = true; + effect = ImpactHardEffect; +}; + +datablock AudioProfile(ImpactHeavyMetalSound) +{ + filename = "fx/armor/heavy_land_metal.wav"; + description = AudioClose3d; + preload = true; + effect = ImpactMetalEffect; +}; + +datablock AudioProfile(ImpactHeavySnowSound) +{ + filename = "fx/armor/heavy_land_snow.wav"; + description = AudioClosest3d; + preload = true; + effect = ImpactSnowEffect; +}; + +datablock AudioProfile(ImpactHeavyWaterEasySound) +{ + filename = "fx/armor/general_water_smallsplash.wav"; + description = AudioClose3d; + preload = true; +}; + +datablock AudioProfile(ImpactHeavyWaterMediumSound) +{ + filename = "fx/armor/general_water_medsplash.wav"; + description = AudioClose3d; + preload = true; +}; + +datablock AudioProfile(ImpactHeavyWaterHardSound) +{ + filename = "fx/armor/general_water_bigsplash.wav"; + description = AudioDefault3d; + preload = true; +}; + +datablock AudioProfile(ExitingWaterHeavySound) +{ + filename = "fx/armor/general_water_exit2.wav"; + description = AudioClose3d; + preload = true; +}; + +//---------------------------------------------------------------------------- +// Splash +//---------------------------------------------------------------------------- + +datablock ParticleData(PlayerSplashMist) +{ + dragCoefficient = 2.0; + gravityCoefficient = -0.05; + inheritedVelFactor = 0.0; + constantAcceleration = 0.0; + lifetimeMS = 400; + lifetimeVarianceMS = 100; + useInvAlpha = false; + spinRandomMin = -90.0; + spinRandomMax = 500.0; + textureName = "particleTest"; + colors[0] = "0.7 0.8 1.0 1.0"; + colors[1] = "0.7 0.8 1.0 0.5"; + colors[2] = "0.7 0.8 1.0 0.0"; + sizes[0] = 0.5; + sizes[1] = 0.5; + sizes[2] = 0.8; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; +}; + +datablock ParticleEmitterData(PlayerSplashMistEmitter) +{ + ejectionPeriodMS = 5; + periodVarianceMS = 0; + ejectionVelocity = 3.0; + velocityVariance = 2.0; + ejectionOffset = 0.0; + thetaMin = 85; + thetaMax = 85; + phiReferenceVel = 0; + phiVariance = 360; + overrideAdvances = false; + lifetimeMS = 250; + particles = "PlayerSplashMist"; +}; + + +datablock ParticleData(PlayerBubbleParticle) +{ + dragCoefficient = 0.0; + gravityCoefficient = -0.50; + inheritedVelFactor = 0.0; + constantAcceleration = 0.0; + lifetimeMS = 400; + lifetimeVarianceMS = 100; + useInvAlpha = false; + textureName = "special/bubbles"; + colors[0] = "0.7 0.8 1.0 0.4"; + colors[1] = "0.7 0.8 1.0 0.4"; + colors[2] = "0.7 0.8 1.0 0.0"; + sizes[0] = 0.1; + sizes[1] = 0.3; + sizes[2] = 0.3; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; +}; + +datablock ParticleEmitterData(PlayerBubbleEmitter) +{ + ejectionPeriodMS = 1; + periodVarianceMS = 0; + ejectionVelocity = 2.0; + ejectionOffset = 0.5; + velocityVariance = 0.5; + thetaMin = 0; + thetaMax = 80; + phiReferenceVel = 0; + phiVariance = 360; + overrideAdvances = false; + particles = "PlayerBubbleParticle"; +}; + +datablock ParticleData(PlayerFoamParticle) +{ + dragCoefficient = 2.0; + gravityCoefficient = -0.05; + inheritedVelFactor = 0.0; + constantAcceleration = 0.0; + lifetimeMS = 400; + lifetimeVarianceMS = 100; + useInvAlpha = false; + spinRandomMin = -90.0; + spinRandomMax = 500.0; + textureName = "particleTest"; + colors[0] = "0.7 0.8 1.0 0.20"; + colors[1] = "0.7 0.8 1.0 0.20"; + colors[2] = "0.7 0.8 1.0 0.00"; + sizes[0] = 0.2; + sizes[1] = 0.4; + sizes[2] = 1.6; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; +}; + +datablock ParticleEmitterData(PlayerFoamEmitter) +{ + ejectionPeriodMS = 10; + periodVarianceMS = 0; + ejectionVelocity = 3.0; + velocityVariance = 1.0; + ejectionOffset = 0.0; + thetaMin = 85; + thetaMax = 85; + phiReferenceVel = 0; + phiVariance = 360; + overrideAdvances = false; + particles = "PlayerFoamParticle"; +}; + + +datablock ParticleData( PlayerFoamDropletsParticle ) +{ + dragCoefficient = 1; + gravityCoefficient = 0.2; + inheritedVelFactor = 0.2; + constantAcceleration = -0.0; + lifetimeMS = 600; + lifetimeVarianceMS = 0; + textureName = "special/droplet"; + colors[0] = "0.7 0.8 1.0 1.0"; + colors[1] = "0.7 0.8 1.0 0.5"; + colors[2] = "0.7 0.8 1.0 0.0"; + sizes[0] = 0.8; + sizes[1] = 0.3; + sizes[2] = 0.0; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; +}; + +datablock ParticleEmitterData( PlayerFoamDropletsEmitter ) +{ + ejectionPeriodMS = 7; + periodVarianceMS = 0; + ejectionVelocity = 2; + velocityVariance = 1.0; + ejectionOffset = 0.0; + thetaMin = 60; + thetaMax = 80; + phiReferenceVel = 0; + phiVariance = 360; + overrideAdvances = false; + orientParticles = true; + particles = "PlayerFoamDropletsParticle"; +}; + + + +datablock ParticleData( PlayerSplashParticle ) +{ + dragCoefficient = 1; + gravityCoefficient = 0.2; + inheritedVelFactor = 0.2; + constantAcceleration = -0.0; + lifetimeMS = 600; + lifetimeVarianceMS = 0; + textureName = "special/droplet"; + colors[0] = "0.7 0.8 1.0 1.0"; + colors[1] = "0.7 0.8 1.0 0.5"; + colors[2] = "0.7 0.8 1.0 0.0"; + sizes[0] = 0.5; + sizes[1] = 0.5; + sizes[2] = 0.5; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; +}; + +datablock ParticleEmitterData( PlayerSplashEmitter ) +{ + ejectionPeriodMS = 1; + periodVarianceMS = 0; + ejectionVelocity = 3; + velocityVariance = 1.0; + ejectionOffset = 0.0; + thetaMin = 60; + thetaMax = 80; + phiReferenceVel = 0; + phiVariance = 360; + overrideAdvances = false; + orientParticles = true; + lifetimeMS = 100; + particles = "PlayerSplashParticle"; +}; + +datablock SplashData(PlayerSplash) +{ + numSegments = 15; + ejectionFreq = 15; + ejectionAngle = 40; + ringLifetime = 0.5; + lifetimeMS = 300; + velocity = 4.0; + startRadius = 0.0; + acceleration = -3.0; + texWrap = 5.0; + + texture = "special/water2"; + + emitter[0] = PlayerSplashEmitter; + emitter[1] = PlayerSplashMistEmitter; + + colors[0] = "0.7 0.8 1.0 0.0"; + colors[1] = "0.7 0.8 1.0 0.3"; + colors[2] = "0.7 0.8 1.0 0.7"; + colors[3] = "0.7 0.8 1.0 0.0"; + times[0] = 0.0; + times[1] = 0.4; + times[2] = 0.8; + times[3] = 1.0; +}; + +//---------------------------------------------------------------------------- +// Jet data +//---------------------------------------------------------------------------- +datablock ParticleData(HumanArmorJetParticle) +{ + dragCoefficient = 0.0; + gravityCoefficient = 0; + inheritedVelFactor = 0.2; + constantAcceleration = 0.0; + lifetimeMS = 100; + lifetimeVarianceMS = 0; + textureName = "particleTest"; + colors[0] = "0.32 0.47 0.47 1.0"; + colors[1] = "0.32 0.47 0.47 0"; + sizes[0] = 0.40; + sizes[1] = 0.15; +}; + +datablock ParticleEmitterData(HumanArmorJetEmitter) +{ + ejectionPeriodMS = 3; + periodVarianceMS = 0; + ejectionVelocity = 3; + velocityVariance = 2.9; + ejectionOffset = 0.0; + thetaMin = 0; + thetaMax = 5; + phiReferenceVel = 0; + phiVariance = 360; + overrideAdvances = false; + particles = "HumanArmorJetParticle"; +}; + +datablock JetEffectData(HumanArmorJetEffect) +{ + texture = "special/jetExhaust02"; + coolColor = "0.0 0.0 1.0 1.0"; + hotColor = "0.2 0.4 0.7 1.0"; + activateTime = 0.2; + deactivateTime = 0.05; + length = 0.75; + width = 0.2; + speed = -15; + stretch = 2.0; + yOffset = 0.2; +}; + +datablock JetEffectData(HumanMediumArmorJetEffect) +{ + texture = "special/jetExhaust02"; + coolColor = "0.0 0.0 1.0 1.0"; + hotColor = "0.2 0.4 0.7 1.0"; + activateTime = 0.2; + deactivateTime = 0.05; + length = 0.75; + width = 0.2; + speed = -15; + stretch = 2.0; + yOffset = 0.4; +}; + +datablock JetEffectData(HumanLightFemaleArmorJetEffect) +{ + texture = "special/jetExhaust02"; + coolColor = "0.0 0.0 1.0 1.0"; + hotColor = "0.2 0.4 0.7 1.0"; + activateTime = 0.2; + deactivateTime = 0.05; + length = 0.75; + width = 0.2; + speed = -15; + stretch = 2.0; + yOffset = 0.2; +}; + +datablock JetEffectData(BiodermArmorJetEffect) +{ + texture = "special/jetExhaust02"; + coolColor = "0.0 0.0 1.0 1.0"; + hotColor = "0.8 0.6 0.2 1.0"; + activateTime = 0.2; + deactivateTime = 0.05; + length = 0.75; + width = 0.2; + speed = -15; + stretch = 2.0; + yOffset = 0.0; +}; + +//---------------------------------------------------------------------------- +// Foot puffs +//---------------------------------------------------------------------------- +datablock ParticleData(LightPuff) +{ + dragCoefficient = 2.0; + gravityCoefficient = -0.01; + inheritedVelFactor = 0.0; + constantAcceleration = 0.0; + lifetimeMS = 500; + lifetimeVarianceMS = 100; + useInvAlpha = true; + spinRandomMin = -35.0; + spinRandomMax = 35.0; + textureName = "particleTest"; + colors[0] = "0.46 0.36 0.26 0.4"; + colors[1] = "0.46 0.46 0.36 0.0"; + sizes[0] = 0.4; + sizes[1] = 1.0; +}; + +datablock ParticleEmitterData(LightPuffEmitter) +{ + ejectionPeriodMS = 35; + periodVarianceMS = 10; + ejectionVelocity = 0.1; + velocityVariance = 0.05; + ejectionOffset = 0.0; + thetaMin = 5; + thetaMax = 20; + phiReferenceVel = 0; + phiVariance = 360; + overrideAdvances = false; + useEmitterColors = true; + particles = "LightPuff"; +}; + +//---------------------------------------------------------------------------- +// Liftoff dust +//---------------------------------------------------------------------------- +datablock ParticleData(LiftoffDust) +{ + dragCoefficient = 1.0; + gravityCoefficient = -0.01; + inheritedVelFactor = 0.0; + constantAcceleration = 0.0; + lifetimeMS = 1000; + lifetimeVarianceMS = 100; + useInvAlpha = true; + spinRandomMin = -90.0; + spinRandomMax = 500.0; + textureName = "particleTest"; + colors[0] = "0.46 0.36 0.26 0.0"; + colors[1] = "0.46 0.46 0.36 0.4"; + colors[2] = "0.46 0.46 0.36 0.0"; + sizes[0] = 0.2; + sizes[1] = 0.6; + sizes[2] = 1.0; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; +}; + +datablock ParticleEmitterData(LiftoffDustEmitter) +{ + ejectionPeriodMS = 5; + periodVarianceMS = 0; + ejectionVelocity = 2.0; + velocityVariance = 0.0; + ejectionOffset = 0.0; + thetaMin = 90; + thetaMax = 90; + phiReferenceVel = 0; + phiVariance = 360; + overrideAdvances = false; + useEmitterColors = true; + particles = "LiftoffDust"; +}; + +//---------------------------------------------------------------------------- + +datablock ParticleData(BiodermArmorJetParticle) : HumanArmorJetParticle +{ + colors[0] = "0.50 0.48 0.36 1.0"; + colors[1] = "0.50 0.48 0.36 0"; +}; + +datablock ParticleEmitterData(BiodermArmorJetEmitter) : HumanArmorJetEmitter +{ + particles = "BiodermArmorJetParticle"; +}; + + +//---------------------------------------------------------------------------- +datablock DecalData(LightMaleFootprint) +{ + sizeX = 0.125; + sizeY = 0.25; + textureName = "special/footprints/L_male"; +}; + +datablock DebrisData( PlayerDebris ) +{ + explodeOnMaxBounce = false; + + elasticity = 0.35; + friction = 0.5; + + lifetime = 4.0; + lifetimeVariance = 0.0; + + minSpinSpeed = 60; + maxSpinSpeed = 600; + + numBounces = 5; + bounceVariance = 0; + + staticOnMaxBounce = true; + gravModifier = 1.0; + + useRadiusMass = true; + baseRadius = 1; + + velocity = 18.0; + velocityVariance = 12.0; +}; + +// z0dd - ZOD, 4/21/02. Altered most of these properties +datablock PlayerData(LightMaleHumanArmor) : LightPlayerDamageProfile +{ + emap = true; + + className = Armor; + shapeFile = "light_male.dts"; + cameraMaxDist = 3; + computeCRC = true; + + canObserve = true; + cmdCategory = "Clients"; + cmdIcon = CMDPlayerIcon; + cmdMiniIconName = "commander/MiniIcons/com_player_grey"; + + hudImageNameFriendly[0] = "gui/hud_playertriangle"; + hudImageNameEnemy[0] = "gui/hud_playertriangle_enemy"; + hudRenderModulated[0] = true; + + hudImageNameFriendly[1] = "commander/MiniIcons/com_flag_grey"; + hudImageNameEnemy[1] = "commander/MiniIcons/com_flag_grey"; + hudRenderModulated[1] = true; + hudRenderAlways[1] = true; + hudRenderCenter[1] = true; + hudRenderDistance[1] = true; + + hudImageNameFriendly[2] = "commander/MiniIcons/com_flag_grey"; + hudImageNameEnemy[2] = "commander/MiniIcons/com_flag_grey"; + hudRenderModulated[2] = true; + hudRenderAlways[2] = true; + hudRenderCenter[2] = true; + hudRenderDistance[2] = true; + + cameraDefaultFov = 90.0; + cameraMinFov = 5.0; + cameraMaxFov = 120.0; + + debrisShapeName = "debris_player.dts"; + debris = playerDebris; + + aiAvoidThis = true; + + minLookAngle = -1.5; + maxLookAngle = 1.5; + maxFreelookAngle = 3.0; + + mass = 90; + drag = 0.134; + maxdrag = 0.3; + density = 19; + maxDamage = 0.66; + maxEnergy = 60; + repairRate = 0.0033; + energyPerDamagePoint = 75.0; // shield energy required to block one point of damage + + rechargeRate = 0.256; + jetForce = 37.28 * 90; + underwaterJetForce = 28.00 * 90 * 1.5; + underwaterVertJetFactor = 1.5; + jetEnergyDrain = 0.90; + underwaterJetEnergyDrain = 0.7; + minJetEnergy = 3; + maxJetHorizontalPercentage = 0.6; + + runForce = 55.20 * 90; + runEnergyDrain = 0; + minRunEnergy = 0; + maxForwardSpeed = 15; + maxBackwardSpeed = 13; + maxSideSpeed = 13; + + maxUnderwaterForwardSpeed = 13; + maxUnderwaterBackwardSpeed = 11; + maxUnderwaterSideSpeed = 11; + + jumpForce = 8.34 * 90; + jumpEnergyDrain = 0; + minJumpEnergy = 0; + jumpDelay = 0; + + recoverDelay = 8; + recoverRunForceScale = 1.4; + + minImpactSpeed = 45; + speedDamageScale = 0.0022; + + jetSound = ArmorJetSound; + wetJetSound = ArmorJetSound; + jetEmitter = HumanArmorJetEmitter; + jetEffect = HumanArmorJetEffect; + + boundingBox = "1.2 1.2 2.3"; + pickupRadius = 0.75; + + // damage location details + boxNormalHeadPercentage = 0.83; + boxNormalTorsoPercentage = 0.49; + boxHeadLeftPercentage = 0; + boxHeadRightPercentage = 1; + boxHeadBackPercentage = 0; + boxHeadFrontPercentage = 1; + + //Foot Prints + decalData = LightMaleFootprint; + decalOffset = 0.25; + + footPuffEmitter = LightPuffEmitter; + footPuffNumParts = 15; + footPuffRadius = 0.25; + + dustEmitter = LiftoffDustEmitter; + + splash = PlayerSplash; + splashVelocity = 4.0; + splashAngle = 67.0; + splashFreqMod = 300.0; + splashVelEpsilon = 0.60; + bubbleEmitTime = 0.4; + splashEmitter[0] = PlayerFoamDropletsEmitter; + splashEmitter[1] = PlayerFoamEmitter; + splashEmitter[2] = PlayerBubbleEmitter; + mediumSplashSoundVelocity = 10.0; + hardSplashSoundVelocity = 20.0; + exitSplashSoundVelocity = 5.0; + + // Controls over slope of runnable/jumpable surfaces + runSurfaceAngle = 70; + jumpSurfaceAngle = 80; + + minJumpSpeed = 20; + maxJumpSpeed = 30; + + noFrictionOnSki = true; + horizMaxSpeed = 500; + horizResistSpeed = 48.74; + horizResistFactor = 0; + maxJetForwardSpeed = 30; + + upMaxSpeed = 52; + upResistSpeed = 20.89; + upResistFactor = 0.35; + + // heat inc'ers and dec'ers + heatDecayPerSec = 1.0 / 4.0; // takes 4 seconds to clear heat sig. + heatIncreasePerSec = 1.0 / 3.0; // takes 3.0 seconds of constant jet to get full heat sig. + + footstepSplashHeight = 0.35; + //Footstep Sounds + LFootSoftSound = LFootLightSoftSound; + RFootSoftSound = RFootLightSoftSound; + LFootHardSound = LFootLightHardSound; + RFootHardSound = RFootLightHardSound; + LFootMetalSound = LFootLightMetalSound; + RFootMetalSound = RFootLightMetalSound; + LFootSnowSound = LFootLightSnowSound; + RFootSnowSound = RFootLightSnowSound; + LFootShallowSound = LFootLightShallowSplashSound; + RFootShallowSound = RFootLightShallowSplashSound; + LFootWadingSound = LFootLightWadingSound; + RFootWadingSound = RFootLightWadingSound; + LFootUnderwaterSound = LFootLightUnderwaterSound; + RFootUnderwaterSound = RFootLightUnderwaterSound; + LFootBubblesSound = LFootLightBubblesSound; + RFootBubblesSound = RFootLightBubblesSound; + movingBubblesSound = ArmorMoveBubblesSound; + waterBreathSound = WaterBreathMaleSound; + + impactSoftSound = ImpactLightSoftSound; + impactHardSound = ImpactLightHardSound; + impactMetalSound = ImpactLightMetalSound; + impactSnowSound = ImpactLightSnowSound; + + skiSoftSound = SkiAllSoftSound; + skiHardSound = SkiAllHardSound; + skiMetalSound = SkiAllMetalSound; + skiSnowSound = SkiAllSnowSound; + + impactWaterEasy = ImpactLightWaterEasySound; + impactWaterMedium = ImpactLightWaterMediumSound; + impactWaterHard = ImpactLightWaterHardSound; + + groundImpactMinSpeed = 14.0; + groundImpactShakeFreq = "3.0 3.0 3.0"; + groundImpactShakeAmp = "1.0 1.0 1.0"; + groundImpactShakeDuration = 0.6; + groundImpactShakeFalloff = 10.0; + + exitingWater = ExitingWaterLightSound; + + maxWeapons = 3; // Max number of different weapons the player can have + maxGrenades = 1; // Max number of different grenades the player can have + maxMines = 1; // Max number of different mines the player can have + + // Inventory restrictions + max[RepairKit] = 1; + max[Mine] = 3; + max[Grenade] = 5; + max[Blaster] = 1; + max[Plasma] = 1; + max[PlasmaAmmo] = 20; + max[Disc] = 1; + max[DiscAmmo] = 15; + max[SniperRifle] = 1; + max[SniperRifleAmmo] = 12; // z0dd - ZOD, 5/07/04. Added sniper uses ammo if gameplay changes in affect. + max[GrenadeLauncher] = 1; + max[GrenadeLauncherAmmo]= 10; + max[Mortar] = 0; + max[MortarAmmo] = 0; + max[MissileLauncher] = 0; + max[MissileLauncherAmmo]= 0; + max[Chaingun] = 1; + max[ChaingunAmmo] = 100; + max[RepairGun] = 1; + max[CloakingPack] = 1; + max[SensorJammerPack] = 1; + max[EnergyPack] = 1; + max[RepairPack] = 1; + max[ShieldPack] = 1; + max[AmmoPack] = 1; + max[SatchelCharge] = 1; + max[MortarBarrelPack] = 0; + max[MissileBarrelPack] = 0; + max[AABarrelPack] = 0; + max[PlasmaBarrelPack] = 0; + max[ELFBarrelPack] = 0; + max[InventoryDeployable]= 0; + max[MotionSensorDeployable] = 1; + max[PulseSensorDeployable] = 1; + max[TurretOutdoorDeployable] = 0; + max[TurretIndoorDeployable] = 0; + max[FlashGrenade] = 5; + max[ConcussionGrenade] = 5; + max[FlareGrenade] = 5; + max[TargetingLaser] = 1; + max[ELFGun] = 1; + max[ShockLance] = 1; + max[CameraGrenade] = 2; + max[Beacon] = 3; + + observeParameters = "0.5 4.5 4.5"; + shieldEffectScale = "0.7 0.7 1.0"; +}; + +//---------------------------------------------------------------------------- + +datablock DecalData(MediumMaleFootprint) +{ + sizeX = 0.125; + sizeY = 0.25; + textureName = "special/footprints/M_male"; +}; + +// z0dd - ZOD, 4/21/02. Altered most of these properties +datablock PlayerData(MediumMaleHumanArmor) : MediumPlayerDamageProfile +{ + emap = true; + + className = Armor; + shapeFile = "medium_male.dts"; + cameraMaxDist = 3; + computeCRC = true; + + canObserve = true; + cmdCategory = "Clients"; + cmdIcon = CMDPlayerIcon; + cmdMiniIconName = "commander/MiniIcons/com_player_grey"; + + hudImageNameFriendly[0] = "gui/hud_playertriangle"; + hudImageNameEnemy[0] = "gui/hud_playertriangle_enemy"; + hudRenderModulated[0] = true; + + hudImageNameFriendly[1] = "commander/MiniIcons/com_flag_grey"; + hudImageNameEnemy[1] = "commander/MiniIcons/com_flag_grey"; + hudRenderModulated[1] = true; + hudRenderAlways[1] = true; + hudRenderCenter[1] = true; + hudRenderDistance[1] = true; + + hudImageNameFriendly[2] = "commander/MiniIcons/com_flag_grey"; + hudImageNameEnemy[2] = "commander/MiniIcons/com_flag_grey"; + hudRenderModulated[2] = true; + hudRenderAlways[2] = true; + hudRenderCenter[2] = true; + hudRenderDistance[2] = true; + + cameraDefaultFov = 90.0; + cameraMinFov = 5.0; + cameraMaxFov = 120.0; + + debrisShapeName = "debris_player.dts"; + debris = playerDebris; + + aiAvoidThis = true; + + minLookAngle = -1.5; + maxLookAngle = 1.5; + maxFreelookAngle = 3.0; + + mass = 130; + drag = 0.2; + maxdrag = 0.4; + density = 20; + maxDamage = 1.1; + maxEnergy = 80; + repairRate = 0.0033; + energyPerDamagePoint = 75.0; // shield energy required to block one point of damage + + rechargeRate = 0.256; + jetForce = 33.79 * 130; + underwaterJetForce = 27.00 * 130 * 1.5; + underwaterVertJetFactor = 1.5; + jetEnergyDrain = 1.1; + underwaterJetEnergyDrain = 0.65; + minJetEnergy = 3; + maxJetHorizontalPercentage = 0.5; + + runForce = 46 * 130; + runEnergyDrain = 0; + minRunEnergy = 0; + maxForwardSpeed = 12; + maxBackwardSpeed = 10; + maxSideSpeed = 10; + + maxUnderwaterForwardSpeed = 10; + maxUnderwaterBackwardSpeed = 8; + maxUnderwaterSideSpeed = 8; + + recoverDelay = 8; + recoverRunForceScale = 0.8; + + // heat inc'ers and dec'ers + heatDecayPerSec = 1.0 / 4.0; // takes 4 seconds to clear heat sig. + heatIncreasePerSec = 1.0 / 3.0; // takes 3.0 seconds of constant jet to get full heat sig. + + jumpForce = 8.5 * 130; + jumpEnergyDrain = 0; + minJumpEnergy = 0; + jumpDelay = 0; + + // Controls over slope of runnable/jumpable surfaces + runSurfaceAngle = 70; + jumpSurfaceAngle = 80; + + minJumpSpeed = 15; + maxJumpSpeed = 25; + + noFrictionOnSki = true; + horizMaxSpeed = 500; + horizResistSpeed = 41.78; + horizResistFactor = 0; + maxJetForwardSpeed = 25; + + upMaxSpeed = 52; + upResistSpeed = 20.89; + upResistFactor = 0.23; + + minImpactSpeed = 45; + speedDamageScale = 0.0023; + + jetSound = ArmorJetSound; + wetJetSound = ArmorWetJetSound; + + jetEmitter = HumanArmorJetEmitter; + jetEffect = HumanMediumArmorJetEffect; + + boundingBox = "1.45 1.45 2.4"; + pickupRadius = 0.75; + + // damage location details + boxNormalHeadPercentage = 0.83; + boxNormalTorsoPercentage = 0.49; + boxHeadLeftPercentage = 0; + boxHeadRightPercentage = 1; + boxHeadBackPercentage = 0; + boxHeadFrontPercentage = 1; + + //Foot Prints + decalData = MediumMaleFootprint; + decalOffset = 0.35; + + footPuffEmitter = LightPuffEmitter; + footPuffNumParts = 15; + footPuffRadius = 0.25; + + dustEmitter = LiftoffDustEmitter; + + splash = PlayerSplash; + splashVelocity = 4.0; + splashAngle = 67.0; + splashFreqMod = 300.0; + splashVelEpsilon = 0.60; + bubbleEmitTime = 0.4; + splashEmitter[0] = PlayerFoamDropletsEmitter; + splashEmitter[1] = PlayerFoamEmitter; + splashEmitter[2] = PlayerBubbleEmitter; + mediumSplashSoundVelocity = 10.0; + hardSplashSoundVelocity = 20.0; + exitSplashSoundVelocity = 5.0; + + footstepSplashHeight = 0.35; + //Footstep Sounds + LFootSoftSound = LFootMediumSoftSound; + RFootSoftSound = RFootMediumSoftSound; + LFootHardSound = LFootMediumHardSound; + RFootHardSound = RFootMediumHardSound; + LFootMetalSound = LFootMediumMetalSound; + RFootMetalSound = RFootMediumMetalSound; + LFootSnowSound = LFootMediumSnowSound; + RFootSnowSound = RFootMediumSnowSound; + LFootShallowSound = LFootMediumShallowSplashSound; + RFootShallowSound = RFootMediumShallowSplashSound; + LFootWadingSound = LFootMediumWadingSound; + RFootWadingSound = RFootMediumWadingSound; + LFootUnderwaterSound = LFootMediumUnderwaterSound; + RFootUnderwaterSound = RFootMediumUnderwaterSound; + LFootBubblesSound = LFootMediumBubblesSound; + RFootBubblesSound = RFootMediumBubblesSound; + movingBubblesSound = ArmorMoveBubblesSound; + waterBreathSound = WaterBreathMaleSound; + + impactSoftSound = ImpactMediumSoftSound; + impactHardSound = ImpactMediumHardSound; + impactMetalSound = ImpactMediumMetalSound; + impactSnowSound = ImpactMediumSnowSound; + + skiSoftSound = SkiAllSoftSound; + skiHardSound = SkiAllHardSound; + skiMetalSound = SkiAllMetalSound; + skiSnowSound = SkiAllSnowSound; + + impactWaterEasy = ImpactMediumWaterEasySound; + impactWaterMedium = ImpactMediumWaterMediumSound; + impactWaterHard = ImpactMediumWaterHardSound; + + groundImpactMinSpeed = 13.0; + groundImpactShakeFreq = "3.5 3.5 3.5"; + groundImpactShakeAmp = "1.0 1.0 1.0"; + groundImpactShakeDuration = 0.7; + groundImpactShakeFalloff = 10.0; + + exitingWater = ExitingWaterMediumSound; + + maxWeapons = 4; // Max number of different weapons the player can have + maxGrenades = 1; // Max number of different grenades the player can have + maxMines = 1; // Max number of different mines the player can have + + // Inventory restrictions + max[RepairKit] = 1; + max[Mine] = 3; + max[Grenade] = 6; + max[Blaster] = 1; + max[Plasma] = 1; + max[PlasmaAmmo] = 40; + max[Disc] = 1; + max[DiscAmmo] = 15; + max[SniperRifle] = 0; + max[SniperRifleAmmo] = 0; // z0dd - ZOD, 5/07/04. Added sniper uses ammo if gameplay changes in affect. + max[GrenadeLauncher] = 1; + max[GrenadeLauncherAmmo]= 15; // z0dd - ZOD, 9/28/02. Was 12 + max[Mortar] = 0; + max[MortarAmmo] = 0; + max[MissileLauncher] = 1; + max[MissileLauncherAmmo]= 4; + max[Chaingun] = 1; + max[ChaingunAmmo] = 150; + max[RepairGun] = 1; + max[CloakingPack] = 0; + max[SensorJammerPack] = 1; + max[EnergyPack] = 1; + max[RepairPack] = 1; + max[ShieldPack] = 1; + max[AmmoPack] = 1; + max[SatchelCharge] = 1; + max[MortarBarrelPack] = 1; + max[MissileBarrelPack] = 1; + max[AABarrelPack] = 1; + max[PlasmaBarrelPack] = 1; + max[ELFBarrelPack] = 1; + max[InventoryDeployable]= 1; + max[MotionSensorDeployable] = 1; + max[PulseSensorDeployable] = 1; + max[TurretOutdoorDeployable] = 1; + max[TurretIndoorDeployable] = 1; + max[FlashGrenade] = 6; + max[ConcussionGrenade] = 6; + max[FlareGrenade] = 6; + max[TargetingLaser] = 1; + max[ELFGun] = 1; + max[ShockLance] = 1; + max[CameraGrenade] = 3; + max[Beacon] = 3; + + observeParameters = "0.5 4.5 4.5"; + + shieldEffectScale = "0.7 0.7 1.0"; +}; + +//---------------------------------------------------------------------------- +datablock DecalData(HeavyMaleFootprint) +{ + sizeX = 0.25; + sizeY = 0.5; + textureName = "special/footprints/H_male"; +}; + +// z0dd - ZOD, 4/21/02. Altered most of these properties +datablock PlayerData(HeavyMaleHumanArmor) : HeavyPlayerDamageProfile +{ + emap = true; + + className = Armor; + shapeFile = "heavy_male.dts"; + cameraMaxDist = 3; + computeCRC = true; + + canObserve = true; + cmdCategory = "Clients"; + cmdIcon = CMDPlayerIcon; + cmdMiniIconName = "commander/MiniIcons/com_player_grey"; + + hudImageNameFriendly[0] = "gui/hud_playertriangle"; + hudImageNameEnemy[0] = "gui/hud_playertriangle_enemy"; + hudRenderModulated[0] = true; + + hudImageNameFriendly[1] = "commander/MiniIcons/com_flag_grey"; + hudImageNameEnemy[1] = "commander/MiniIcons/com_flag_grey"; + hudRenderModulated[1] = true; + hudRenderAlways[1] = true; + hudRenderCenter[1] = true; + hudRenderDistance[1] = true; + + hudImageNameFriendly[2] = "commander/MiniIcons/com_flag_grey"; + hudImageNameEnemy[2] = "commander/MiniIcons/com_flag_grey"; + hudRenderModulated[2] = true; + hudRenderAlways[2] = true; + hudRenderCenter[2] = true; + hudRenderDistance[2] = true; + + cameraDefaultFov = 90.0; + cameraMinFov = 5.0; + cameraMaxFov = 120.0; + + debrisShapeName = "debris_player.dts"; + debris = playerDebris; + + aiAvoidThis = true; + + minLookAngle = -1.5; + maxLookAngle = 1.5; + maxFreelookAngle = 3.0; + + mass = 180; + drag = 0.23; + maxdrag = 0.5; + density = 27; + maxDamage = 1.32; + maxEnergy = 110; + repairRate = 0.0033; + energyPerDamagePoint = 54.0; // shield energy required to block one point of damage + + rechargeRate = 0.256; + jetForce = 29.58 * 180; + underwaterJetForce = 25.25 * 180 * 1.5; + underwaterVertJetFactor = 1.5; + jetEnergyDrain = 1.25; + underwaterJetEnergyDrain = 0.65; + minJetEnergy = 3; + maxJetHorizontalPercentage = 0.6; + + runForce = 40.25 * 180; + runEnergyDrain = 0; + minRunEnergy = 0; + maxForwardSpeed = 7.5; + maxBackwardSpeed = 5; + maxSideSpeed = 5; + + maxUnderwaterForwardSpeed = 5; + maxUnderwaterBackwardSpeed = 3; + maxUnderwaterSideSpeed = 3; + + recoverDelay = 8; + recoverRunForceScale = 0.2; + + jumpForce = 8.35 * 180; + jumpEnergyDrain = 0; + minJumpEnergy = 0; + jumpDelay = 0; + + // heat inc'ers and dec'ers + heatDecayPerSec = 1.0 / 4.0; // takes 4 seconds to clear heat sig. + heatIncreasePerSec = 1.0 / 3.0; // takes 3.0 seconds of constant jet to get full heat sig. + + // Controls over slope of runnable/jumpable surfaces + runSurfaceAngle = 70; + jumpSurfaceAngle = 75; + + minJumpSpeed = 20; + maxJumpSpeed = 30; + + noFrictionOnSki = true; + horizMaxSpeed = 500; + horizResistSpeed = 34.81; + horizResistFactor = 0; + maxJetForwardSpeed = 20; + + upMaxSpeed = 52; + upResistSpeed = 20.89; + upResistFactor = 0.18; + + minImpactSpeed = 45; + speedDamageScale = 0.0030; + + jetSound = ArmorJetSound; + wetJetSound = ArmorJetSound; + jetEmitter = HumanArmorJetEmitter; + + boundingBox = "1.63 1.63 2.6"; + pickupRadius = 0.75; + + // damage location details + boxNormalHeadPercentage = 0.83; + boxNormalTorsoPercentage = 0.49; + boxHeadLeftPercentage = 0; + boxHeadRightPercentage = 1; + boxHeadBackPercentage = 0; + boxHeadFrontPercentage = 1; + + //Foot Prints + decalData = HeavyMaleFootprint; + decalOffset = 0.4; + + footPuffEmitter = LightPuffEmitter; + footPuffNumParts = 15; + footPuffRadius = 0.25; + + dustEmitter = LiftoffDustEmitter; + + splash = PlayerSplash; + splashVelocity = 4.0; + splashAngle = 67.0; + splashFreqMod = 300.0; + splashVelEpsilon = 0.60; + bubbleEmitTime = 0.4; + splashEmitter[0] = PlayerFoamDropletsEmitter; + splashEmitter[1] = PlayerFoamEmitter; + splashEmitter[2] = PlayerBubbleEmitter; + mediumSplashSoundVelocity = 10.0; + hardSplashSoundVelocity = 20.0; + exitSplashSoundVelocity = 5.0; + + footstepSplashHeight = 0.35; + //Footstep Sounds + LFootSoftSound = LFootHeavySoftSound; + RFootSoftSound = RFootHeavySoftSound; + LFootHardSound = LFootHeavyHardSound; + RFootHardSound = RFootHeavyHardSound; + LFootMetalSound = LFootHeavyMetalSound; + RFootMetalSound = RFootHeavyMetalSound; + LFootSnowSound = LFootHeavySnowSound; + RFootSnowSound = RFootHeavySnowSound; + LFootShallowSound = LFootHeavyShallowSplashSound; + RFootShallowSound = RFootHeavyShallowSplashSound; + LFootWadingSound = LFootHeavyWadingSound; + RFootWadingSound = RFootHeavyWadingSound; + LFootUnderwaterSound = LFootHeavyUnderwaterSound; + RFootUnderwaterSound = RFootHeavyUnderwaterSound; + LFootBubblesSound = LFootHeavyBubblesSound; + RFootBubblesSound = RFootHeavyBubblesSound; + movingBubblesSound = ArmorMoveBubblesSound; + waterBreathSound = WaterBreathMaleSound; + + impactSoftSound = ImpactHeavySoftSound; + impactHardSound = ImpactHeavyHardSound; + impactMetalSound = ImpactHeavyMetalSound; + impactSnowSound = ImpactHeavySnowSound; + + skiSoftSound = SkiAllSoftSound; + skiHardSound = SkiAllHardSound; + skiMetalSound = SkiAllMetalSound; + skiSnowSound = SkiAllSnowSound; + + impactWaterEasy = ImpactHeavyWaterEasySound; + impactWaterMedium = ImpactHeavyWaterMediumSound; + impactWaterHard = ImpactHeavyWaterHardSound; + + groundImpactMinSpeed = 11.0; + groundImpactShakeFreq = "4.0 4.0 4.0"; + groundImpactShakeAmp = "1.0 1.0 1.0"; + groundImpactShakeDuration = 0.8; + groundImpactShakeFalloff = 10.0; + + exitingWater = ExitingWaterHeavySound; + + maxWeapons = 5; // Max number of different weapons the player can have + maxGrenades = 1; // Max number of different grenades the player can have + maxMines = 1; // Max number of different mines the player can have + + // Inventory restrictions + max[RepairKit] = 1; + max[Mine] = 3; + max[Grenade] = 8; + max[Blaster] = 1; + max[Plasma] = 1; + max[PlasmaAmmo] = 50; + max[Disc] = 1; + max[DiscAmmo] = 15; + max[SniperRifle] = 0; + max[SniperRifleAmmo] = 0; // z0dd - ZOD, 5/07/04. Added sniper uses ammo if gameplay changes in affect. + max[GrenadeLauncher] = 1; + max[GrenadeLauncherAmmo]= 15; + max[Mortar] = 1; + max[MortarAmmo] = 10; + max[MissileLauncher] = 1; + max[MissileLauncherAmmo]= 8; + max[Chaingun] = 1; + max[ChaingunAmmo] = 200; + max[RepairGun] = 1; + max[CloakingPack] = 0; + max[SensorJammerPack] = 1; + max[EnergyPack] = 1; + max[RepairPack] = 1; + max[ShieldPack] = 1; + max[AmmoPack] = 1; + max[SatchelCharge] = 1; + max[MortarBarrelPack] = 1; + max[MissileBarrelPack] = 1; + max[AABarrelPack] = 1; + max[PlasmaBarrelPack] = 1; + max[ELFBarrelPack] = 1; + max[InventoryDeployable]= 1; + max[MotionSensorDeployable] = 1; + max[PulseSensorDeployable] = 1; + max[TurretOutdoorDeployable] = 1; + max[TurretIndoorDeployable] = 1; + max[FlashGrenade] = 8; + max[ConcussionGrenade] = 8; + max[FlareGrenade] = 8; + max[TargetingLaser] = 1; + max[ELFGun] = 1; + max[ShockLance] = 1; + max[CameraGrenade] = 3; + max[Beacon] = 3; + + observeParameters = "0.5 4.5 4.5"; + + shieldEffectScale = "0.7 0.7 1.0"; +}; + +//---------------------------------------------------------------------------- +datablock PlayerData(LightFemaleHumanArmor) : LightMaleHumanArmor +{ + shapeFile = "light_female.dts"; + waterBreathSound = WaterBreathFemaleSound; + jetEffect = HumanMediumArmorJetEffect; +}; + +//---------------------------------------------------------------------------- +datablock PlayerData(MediumFemaleHumanArmor) : MediumMaleHumanArmor +{ + shapeFile = "medium_female.dts"; + waterBreathSound = WaterBreathFemaleSound; + jetEffect = HumanArmorJetEffect; +}; + +//---------------------------------------------------------------------------- +datablock PlayerData(HeavyFemaleHumanArmor) : HeavyMaleHumanArmor +{ + shapeFile = "heavy_male.dts"; + waterBreathSound = WaterBreathFemaleSound; +}; + +//---------------------------------------------------------------------------- +datablock DecalData(LightBiodermFootprint) +{ + sizeX = 0.25; + sizeY = 0.25; + textureName = "special/footprints/L_bioderm"; +}; + +datablock PlayerData(LightMaleBiodermArmor) : LightMaleHumanArmor +{ + shapeFile = "bioderm_light.dts"; + jetEmitter = BiodermArmorJetEmitter; + jetEffect = BiodermArmorJetEffect; + + + debrisShapeName = "bio_player_debris.dts"; + + //Foot Prints + decalData = LightBiodermFootprint; + decalOffset = 0.3; + + waterBreathSound = WaterBreathBiodermSound; +}; + +//---------------------------------------------------------------------------- +datablock DecalData(MediumBiodermFootprint) +{ + sizeX = 0.25; + sizeY = 0.25; + textureName = "special/footprints/M_bioderm"; +}; + +datablock PlayerData(MediumMaleBiodermArmor) : MediumMaleHumanArmor +{ + shapeFile = "bioderm_medium.dts"; + jetEmitter = BiodermArmorJetEmitter; + jetEffect = BiodermArmorJetEffect; + + debrisShapeName = "bio_player_debris.dts"; + + //Foot Prints + decalData = MediumBiodermFootprint; + decalOffset = 0.35; + + waterBreathSound = WaterBreathBiodermSound; +}; + +//---------------------------------------------------------------------------- +datablock DecalData(HeavyBiodermFootprint) +{ + sizeX = 0.25; + sizeY = 0.5; + textureName = "special/footprints/H_bioderm"; +}; + +datablock PlayerData(HeavyMaleBiodermArmor) : HeavyMaleHumanArmor +{ + emap = false; + + shapeFile = "bioderm_heavy.dts"; + jetEmitter = BiodermArmorJetEmitter; + + debrisShapeName = "bio_player_debris.dts"; + + //Foot Prints + decalData = HeavyBiodermFootprint; + decalOffset = 0.4; + + waterBreathSound = WaterBreathBiodermSound; +}; + + +//---------------------------------------------------------------------------- + +function Armor::onAdd(%data,%obj) +{ + Parent::onAdd(%data, %obj); + // Vehicle timeout + %obj.mountVehicle = true; + + // Default dynamic armor stats + %obj.setRechargeRate(%data.rechargeRate); + %obj.setRepairRate(0); + + %obj.setSelfPowered(); +} + +function Armor::onRemove(%this, %obj) +{ + //Frohny asked me to remove this - all players are deleted now on mission cycle... + //if(%obj.getState() !$= "Dead") + //{ + // error("ERROR PLAYER REMOVED WITHOUT DEATH - TRACE:"); + // trace(1); + // schedule(0,0,trace,0); + //} + + if (%obj.client.player == %obj) + %obj.client.player = 0; +} + +function Armor::onNewDataBlock(%this,%obj) +{ +} + +//------------------------------------------------------------------------------------- +// z0dd - ZOD, 4-15-02. Allow respawn switching during tourney wait. Function rewrite. +function Armor::onDisabled(%this,%obj,%state) +{ + if($MatchStarted) + { + %obj.startFade( 1000, $CorpseTimeoutValue - 1000, true ); + %obj.schedule( $CorpseTimeoutValue, "delete" ); + } + else + { + %obj.schedule( 150, "delete" ); + } +} +//------------------------------------------------------------------------------------- + +function Armor::shouldApplyImpulse(%data, %obj) +{ + return true; +} + +$wasFirstPerson = true; + +function Armor::onMount(%this,%obj,%vehicle,%node) +{ + if (%node == 0) + { + // Node 0 is the pilot's pos. + %obj.setTransform("0 0 0 0 0 1 0"); + %obj.setActionThread(%vehicle.getDatablock().mountPose[%node],true,true); + // ilys - Fix for the Standing Pilot bug + %obj.schedule(300,"setActionThread",%vehicle.getDatablock().mountPose[%node],true,true); + + if(!%obj.inStation) + %obj.lastWeapon = (%obj.getMountedImage($WeaponSlot) == 0 ) ? "" : %obj.getMountedImage($WeaponSlot).item; + + %obj.unmountImage($WeaponSlot); + + if(!%obj.client.isAIControlled()) + { + %obj.setControlObject(%vehicle); + %obj.client.setObjectActiveImage(%vehicle, 2); + } + + //E3 respawn... + + if(%obj == %obj.lastVehicle.lastPilot && %obj.lastVehicle != %vehicle) + { + schedule(15000, %obj.lastVehicle,"vehicleAbandonTimeOut", %obj.lastVehicle); + %obj.lastVehicle.lastPilot = ""; + } + if(%vehicle.lastPilot !$= "" && %vehicle == %vehicle.lastPilot.lastVehicle) + %vehicle.lastPilot.lastVehicle = ""; + + %vehicle.abandon = false; + %vehicle.lastPilot = %obj; + %obj.lastVehicle = %vehicle; + + // update the vehicle's team + if((%vehicle.getTarget() != -1) && %vehicle.getDatablock().cantTeamSwitch $= "") + { + setTargetSensorGroup(%vehicle.getTarget(), %obj.client.getSensorGroup()); + if( %vehicle.turretObject > 0 ) + setTargetSensorGroup(%vehicle.turretObject.getTarget(), %obj.client.getSensorGroup()); + } + + // Send a message to the client so they can decide if they want to change view or not: + commandToClient( %obj.client, 'VehicleMount' ); + + } + else + { + // tailgunner/passenger positions + if(%vehicle.getDataBlock().mountPose[%node] !$= "") + %obj.setActionThread(%vehicle.getDatablock().mountPose[%node]); + else + %obj.setActionThread("root", true); + } + // z0dd - ZOD, 6/27/02. announce to any other passengers that you've boarded + if(%vehicle.getDatablock().numMountPoints > 1) + { + %nodeName = findNodeName(%vehicle, %node); // function in vehicle.cs + for(%i = 0; %i < %vehicle.getDatablock().numMountPoints; %i++) + { + if (%vehicle.getMountNodeObject(%i) > 0) + { + if(%vehicle.getMountNodeObject(%i).client != %obj.client) + { + %team = (%obj.team == %vehicle.getMountNodeObject(%i).client.team ? 'Teammate' : 'Enemy'); + messageClient( %vehicle.getMountNodeObject(%i).client, 'MsgShowPassenger', '\c2%3: \c3%1\c2 has boarded in the \c3%2\c2 position.', %obj.client.name, %nodeName, %team ); + } + commandToClient( %vehicle.getMountNodeObject(%i).client, 'showPassenger', %node, true); + } + } + } + //make sure they don't have any packs active +// if ( %obj.getImageState( $BackpackSlot ) $= "activate") +// %obj.use("Backpack"); + if ( %obj.getImageTrigger( $BackpackSlot ) ) + %obj.setImageTrigger( $BackpackSlot, false ); + + //AI hooks + %obj.client.vehicleMounted = %vehicle; + AIVehicleMounted(%vehicle); + if(%obj.client.isAIControlled()) + %this.AIonMount(%obj, %vehicle, %node); +} + + +function Armor::onUnmount( %this, %obj, %vehicle, %node ) +{ + if ( %node == 0 ) + { + commandToClient( %obj.client, 'VehicleDismount' ); + commandToClient(%obj.client, 'removeReticle'); + + if(%obj.inv[%obj.lastWeapon]) + %obj.use(%obj.lastWeapon); + else + { + if(%obj.getMountedImage($WeaponSlot) == 0) + %obj.selectWeaponSlot( 0 ); + } + //Inform gunner position when pilot leaves... + //if(%vehicle.getDataBlock().showPilotInfo !$= "") + // if((%gunner = %vehicle.getMountNodeObject(1)) != 0) + // commandToClient(%gunner.client, 'PilotInfo', "PILOT EJECTED", 6, 1); + } + // z0dd - ZOD, 6/27/02. announce to any other passengers that you've left + if(%vehicle.getDatablock().numMountPoints > 1) + { + %nodeName = findNodeName(%vehicle, %node); // function in vehicle.cs + for(%i = 0; %i < %vehicle.getDatablock().numMountPoints; %i++) + { + if (%vehicle.getMountNodeObject(%i) > 0) + { + if(%vehicle.getMountNodeObject(%i).client != %obj.client) + { + %team = (%obj.team == %vehicle.getMountNodeObject(%i).client.team ? 'Teammate' : 'Enemy'); + messageClient( %vehicle.getMountNodeObject(%i).client, 'MsgShowPassenger', '\c2%3: \c3%1\c2 has ejected from the \c3%2\c2 position.', %obj.client.name, %nodeName, %team ); + } + commandToClient( %vehicle.getMountNodeObject(%i).client, 'showPassenger', %node, false); + } + } + } + //AI hooks + %obj.client.vehicleMounted = ""; + if(%obj.client.isAIControlled()) + %this.AIonUnMount(%obj, %vehicle, %node); +} + +$ammoType[0] = "PlasmaAmmo"; +$ammoType[1] = "DiscAmmo"; +$ammoType[2] = "GrenadeLauncherAmmo"; +$ammoType[3] = "MortarAmmo"; +$ammoType[4] = "MissileLauncherAmmo"; +$ammoType[5] = "ChaingunAmmo"; +// ------------------------------------- +// z0dd - ZOD, 9/13/02. For TR2 weapons +$ammoType[6] = "TR2DiscAmmo"; +$ammoType[7] = "TR2GrenadeLauncherAmmo"; +$ammoType[8] = "TR2ChaingunAmmo"; +$ammoType[9] = "TR2MortarAmmo"; +// ------------------------------------- +// z0dd - ZOD, 5/07/04. Added sniper uses ammo if gameplay changes in affect. +$ammoType[10] = "SniperRifleAmmo"; + +function Armor::onCollision(%this,%obj,%col,%forceVehicleNode) +{ + if (%obj.getState() $= "Dead") + return; + + %dataBlock = %col.getDataBlock(); + %className = %dataBlock.className; + %client = %obj.client; + // player collided with a vehicle + %node = -1; + if (%forceVehicleNode !$= "" || (%className $= WheeledVehicleData || %className $= FlyingVehicleData || %className $= HoverVehicleData) && + %obj.mountVehicle && %obj.getState() $= "Move" && %col.mountable && !%obj.inStation && %col.getDamageState() !$= "Destroyed") { + + //if the player is an AI, he should snap to the mount points in node order, + //to ensure they mount the turret before the passenger seat, regardless of where they collide... + if (%obj.client.isAIControlled()) + { + %transform = %col.getTransform(); + + //either the AI is *required* to pilot, or they'll pick the first available passenger seat + if (%client.pilotVehicle) + { + //make sure the bot is in light armor + if (%client.player.getArmorSize() $= "Light" || %obj.getArmorSize() $= "Medium") + { + //make sure the pilot seat is empty + if (!%col.getMountNodeObject(0)) + %node = 0; + } + } + else + %node = findAIEmptySeat(%col, %obj); + } + else + %node = findEmptySeat(%col, %obj, %forceVehicleNode); + + //now mount the player in the vehicle + if(%node >= 0) + { + // players can't be pilots, bombardiers or turreteers if they have + // "large" packs -- stations, turrets, turret barrels + if(hasLargePack(%obj)) + { + // check to see if attempting to enter a "sitting" node + if(nodeIsSitting(%datablock, %node)) + { + // send the player a message -- can't sit here with large pack + if(!%obj.noSitMessage) + { + %obj.noSitMessage = true; + %obj.schedule(2000, "resetSitMessage"); + messageClient(%obj.client, 'MsgCantSitHere', '\c2Pack too large, can\'t occupy this seat.~wfx/misc/misc.error.wav'); + } + return; + } + } + if(%col.noEnemyControl && %obj.team != %col.team) + return; + + commandToClient(%obj.client,'SetDefaultVehicleKeys', true); + //If pilot or passenger then bind a few extra keys + if(%node == 0) + commandToClient(%obj.client,'SetPilotVehicleKeys', true); + else + commandToClient(%obj.client,'SetPassengerVehicleKeys', true); + + if(!%obj.inStation) + %col.lastWeapon = ( %col.getMountedImage($WeaponSlot) == 0 ) ? "" : %col.getMountedImage($WeaponSlot).item; + else + %col.lastWeapon = %obj.lastWeapon; + + // z0dd - ZOD, 4/10/04. Lagg_Alot - AI Hook here to state sitting position for vehicle type to fix the AI standing is seat bug. + if (%obj.client.isAIControlled() && %node == 1 && (%type $= "BomberFlyer" || %type $= "AssaultVehicle")) + { + //%client.player.setActionThread(%col.getDataBlock().mountPose[0], true, true); + %client.player.setActionThread(sitting, true, true); + } + + %col.mountObject(%obj,%node); + %col.playAudio(0, MountVehicleSound); + %obj.mVehicle = %col; + + // if player is repairing something, stop it + if(%obj.repairing) + stopRepairing(%obj); + + //this will setup the huds as well... + %dataBlock.playerMounted(%col,%obj, %node); + } + } + else if (%className $= "Armor") { + // player has collided with another player + if(%col.getState() $= "Dead") { + %gotSomething = false; + // it's corpse-looting time! + // weapons -- don't pick up more than you are allowed to carry! + for(%i = 0; ( %obj.weaponCount < %obj.getDatablock().maxWeapons ) && $InvWeapon[%i] !$= ""; %i++) + { + %weap = $NameToInv[$InvWeapon[%i]]; + if ( %col.hasInventory( %weap ) ) + { + if ( %obj.incInventory(%weap, 1) > 0 ) + { + %col.decInventory(%weap, 1); + %gotSomething = true; + messageClient(%obj.client, 'MsgItemPickup', '\c0You picked up %1.', %weap.pickUpName); + } + } + } + // targeting laser: + if ( %col.hasInventory( "TargetingLaser" ) ) + { + if ( %obj.incInventory( "TargetingLaser", 1 ) > 0 ) + { + %col.decInventory( "TargetingLaser", 1 ); + %gotSomething = true; + messageClient( %obj.client, 'MsgItemPickup', '\c0You picked up a targeting laser.' ); + } + } + // ammo + for(%j = 0; $ammoType[%j] !$= ""; %j++) + { + %ammoAmt = %col.inv[$ammoType[%j]]; + if(%ammoAmt) + { + // incInventory returns the amount of stuff successfully grabbed + %grabAmt = %obj.incInventory($ammoType[%j], %ammoAmt); + if(%grabAmt > 0) + { + %col.decInventory($ammoType[%j], %grabAmt); + %gotSomething = true; + messageClient(%obj.client, 'MsgItemPickup', '\c0You picked up %1.', $ammoType[%j].pickUpName); + %obj.client.setWeaponsHudAmmo($ammoType[%j], %obj.getInventory($ammoType[%j])); + } + } + } + // figure out what type, if any, grenades the (live) player has + %playerGrenType = "None"; + for(%x = 0; $InvGrenade[%x] !$= ""; %x++) { + %gren = $NameToInv[$InvGrenade[%x]]; + %playerGrenAmt = %obj.inv[%gren]; + if(%playerGrenAmt > 0) + { + %playerGrenType = %gren; + break; + } + } + // grenades + for(%k = 0; $InvGrenade[%k] !$= ""; %k++) + { + %gren = $NameToInv[$InvGrenade[%k]]; + %corpseGrenAmt = %col.inv[%gren]; + // does the corpse hold any of this grenade type? + if(%corpseGrenAmt) + { + // can the player pick up this grenade type? + if((%playerGrenType $= "None") || (%playerGrenType $= %gren)) + { + %taken = %obj.incInventory(%gren, %corpseGrenAmt); + if(%taken > 0) + { + %col.decInventory(%gren, %taken); + %gotSomething = true; + messageClient(%obj.client, 'MsgItemPickup', '\c0You picked up %1.', %gren.pickUpName); + %obj.client.setInventoryHudAmount(%gren, %obj.getInventory(%gren)); + } + } + break; + } + } + // figure out what type, if any, mines the (live) player has + %playerMineType = "None"; + for(%y = 0; $InvMine[%y] !$= ""; %y++) + { + %mType = $NameToInv[$InvMine[%y]]; + %playerMineAmt = %obj.inv[%mType]; + if(%playerMineAmt > 0) + { + %playerMineType = %mType; + break; + } + } + // mines + for(%l = 0; $InvMine[%l] !$= ""; %l++) + { + %mine = $NameToInv[$InvMine[%l]]; + %mineAmt = %col.inv[%mine]; + if(%mineAmt) { + if((%playerMineType $= "None") || (%playerMineType $= %mine)) + { + %grabbed = %obj.incInventory(%mine, %mineAmt); + if(%grabbed > 0) + { + %col.decInventory(%mine, %grabbed); + %gotSomething = true; + messageClient(%obj.client, 'MsgItemPickup', '\c0You picked up %1.', %mine.pickUpName); + %obj.client.setInventoryHudAmount(%mine, %obj.getInventory(%mine)); + } + } + break; + } + } + // beacons + %beacAmt = %col.inv[Beacon]; + if(%beacAmt) + { + %bTaken = %obj.incInventory(Beacon, %beacAmt); + if(%bTaken > 0) + { + %col.decInventory(Beacon, %bTaken); + %gotSomething = true; + messageClient(%obj.client, 'MsgItemPickup', '\c0You picked up %1.', Beacon.pickUpName); + %obj.client.setInventoryHudAmount(Beacon, %obj.getInventory(Beacon)); + } + } + // repair kit + %rkAmt = %col.inv[RepairKit]; + if(%rkAmt) + { + %rkTaken = %obj.incInventory(RepairKit, %rkAmt); + if(%rkTaken > 0) + { + %col.decInventory(RepairKit, %rkTaken); + %gotSomething = true; + messageClient(%obj.client, 'MsgItemPickup', '\c0You picked up %1.', RepairKit.pickUpName); + %obj.client.setInventoryHudAmount(RepairKit, %obj.getInventory(RepairKit)); + } + } + } + if(%gotSomething) + %col.playAudio(0, CorpseLootingSound); + } +} + +// ------------------------------------------------------------ +// z0dd - ZOD, 9/27/02. Delay on grabbing flag after tossing it +function Player::resetFlagTossWait(%this) +{ + %this.flagTossWait = false; +} +// ------------------------------------------------------------ + +function Player::resetSitMessage(%obj) +{ + %obj.noSitMessage = false; +} + +function Player::setInvincible(%this, %val) +{ + %this.invincible = %val; +} + +function Player::causedRecentDamage(%this, %val) +{ + %this.causedRecentDamage = %val; +} + +function hasLargePack(%player) +{ + %pack = %player.getMountedImage($BackpackSlot); + if(%pack.isLarge) + return true; + else + return false; +} + +function nodeIsSitting(%vehDBlock, %node) +{ + // pilot == always a "sitting" node + if(%node == 0) + return true; + else { + switch$ (%vehDBlock.getName()) + { + // note: for assault tank -- both nodes are sitting + // for any single-user vehicle -- pilot node is sitting + case "BomberFlyer": + // bombardier == sitting; tailgunner == not sitting + if(%node == 1) + return true; + else + return false; + case "HAPCFlyer": + // only the pilot node is sitting + return false; + default: + return true; + } + } +} + +//---------------------------------------------------------------------------- +function Player::setMountVehicle(%this, %val) +{ + %this.mountVehicle = %val; +} + +function Armor::doDismount(%this, %obj, %forced) +{ + // This function is called by player.cc when the jump trigger + // is true while mounted + if (!%obj.isMounted()) + return; + + if(isObject(%obj.getObjectMount().shield)) + %obj.getObjectMount().shield.delete(); + + commandToClient(%obj.client,'SetDefaultVehicleKeys', false); + + // Position above dismount point + + %pos = getWords(%obj.getTransform(), 0, 2); + %oldPos = %pos; + %vec[0] = " 0 0 1"; + %vec[1] = " 0 0 1"; + %vec[2] = " 0 0 -1"; + %vec[3] = " 1 0 0"; + %vec[4] = "-1 0 0"; + %numAttempts = 5; + %success = -1; + %impulseVec = "0 0 0"; + if (%obj.getObjectMount().getDatablock().hasDismountOverrides() == true) + { + %vec[0] = %obj.getObjectMount().getDatablock().getDismountOverride(%obj.getObjectMount(), %obj); + %vec[0] = MatrixMulVector(%obj.getObjectMount().getTransform(), %vec[0]); + } + else + { + %vec[0] = MatrixMulVector( %obj.getTransform(), %vec[0]); + } + + %pos = "0 0 0"; + for (%i = 0; %i < %numAttempts; %i++) + { + %pos = VectorAdd(%oldPos, VectorScale(%vec[%i], 5)); // z0dd - ZOD, 4/24/02. More ejection clearance. 5 was 3 + if (%obj.checkDismountPoint(%oldPos, %pos)) + { + %success = %i; + %impulseVec = %vec[%i]; + break; + } + } + if (%forced && %success == -1) + { + %pos = %oldPos; + } + + // hide the dashboard HUD and delete elements based on node + commandToClient(%obj.client, 'setHudMode', 'Standard', "", 0); + // Unmount and control body + if(%obj.vehicleTurret) + %obj.vehicleTurret.getDataBlock().playerDismount(%obj.vehicleTurret); + %obj.unmount(); + if(%obj.mVehicle) + %obj.mVehicle.getDataBlock().playerDismounted(%obj.mVehicle, %obj); + + // bots don't change their control objects when in vehicles + if(!%obj.client.isAIControlled()) + { + %vehicle = %obj.getControlObject(); + %obj.setControlObject(0); + } + + %obj.mountVehicle = false; + %obj.schedule(1500, "setMountVehicle", true); // z0dd - ZOD, 3/29/02. Reduce time between being able to mount vehicles . Was 4000 + + // Position above dismount point + %obj.setTransform(%pos); + %obj.playAudio(0, UnmountVehicleSound); + %obj.applyImpulse(%pos, VectorScale(%impulseVec, %obj.getDataBlock().mass * 3)); + %obj.setPilot(false); + %obj.vehicleTurret = ""; + + // ----------------------------------------------------- + // z0dd - ZOD, 4/8/02. Set player velocity when ejecting + %vel = %obj.getVelocity(); + %vec = vectorDot(%vel, vectorNormalize(%vel)); + if(%vec > 50) + { + %scale = 50 / %vec; + %obj.setVelocity(VectorScale(%vel, %scale)); + } + // ----------------------------------------------------- +} + +function resetObserveFollow( %client, %dismount ) +{ + if( %dismount ) + { + if( !isObject( %client.player ) ) + return; + + for( %i = 0; %i < %client.observeCount; %i++ ) + { + // z0dd - ZOD, 5/21/03. Make sure this client actually obs this client + if ( %client.observers[%i].clientObserve != %client ) + continue; + + %client.observers[%i].camera.setOrbitMode( %client.player, %client.player.getTransform(), 0.5, 4.5, 4.5); + } + } + else + { + if( !%client.player.isMounted() ) + return; + + // grab the vehicle... + %mount = %client.player.getObjectMount(); + if( %mount.getDataBlock().observeParameters $= "" ) + %params = %client.player.getTransform(); + else + %params = %mount.getDataBlock().observeParameters; + + for( %i = 0; %i < %client.observeCount; %i++ ) + { + // z0dd - ZOD, 5/21/03. Make sure this client actually obs this client + if ( %client.observers[%i].clientObserve != %client ) + continue; + + %client.observers[%i].camera.setOrbitMode(%mount, %mount.getTransform(), getWord( %params, 0 ), getWord( %params, 1 ), getWord( %params, 2 )); + } + } +} + + +//---------------------------------------------------------------------------- + +function Player::scriptKill(%player, %damageType) +{ + %player.scriptKilled = 1; + %player.setInvincible(false); + %player.damage(0, %player.getPosition(), 10000, %damageType); +} + +function Armor::damageObject(%data, %targetObject, %sourceObject, %position, %amount, %damageType, %momVec, %mineSC) +{ +//error("Armor::damageObject( "@%data@", "@%targetObject@", "@%sourceObject@", "@%position@", "@%amount@", "@%damageType@", "@%momVec@" )"); + if(%targetObject.invincible || %targetObject.getState() $= "Dead") + return; + + //---------------------------------------------------------------- + // z0dd - ZOD, 6/09/02. Check to see if this vehicle is destroyed, + // if it is do no damage. Fixes vehicle ghosting bug. We do not + // check for isObject here, destroyed objects fail it even though + // they exist as objects, go figure. + if(%damageType == $DamageType::Impact) + if(%sourceObject.getDamageState() $= "Destroyed") + return; + + if (%targetObject.isMounted() && %targetObject.scriptKilled $= "") + { + %mount = %targetObject.getObjectMount(); + if(%mount.team == %targetObject.team) + { + %found = -1; + for (%i = 0; %i < %mount.getDataBlock().numMountPoints; %i++) + { + if (%mount.getMountNodeObject(%i) == %targetObject) + { + %found = %i; + break; + } + } + + if (%found != -1) + { + if (%mount.getDataBlock().isProtectedMountPoint[%found]) + { + // z0dd - ZOD, 5/07/04. Let players be damaged if gameplay changes in affect. + if(!$Host::ClassicLoadPlayerChanges) + { + %mount.getDataBlock().damageObject(%mount, %sourceObject, %position, %amount, %damageType); + return; + } + else + { + if(%damageType != $DamageType::Laser && %damageType != $DamageType::Bullet && %damageType != $DamageType::Blaster) + return; + } + } + } + } + } + + %targetClient = %targetObject.getOwnerClient(); + if(isObject(%mineSC)) + %sourceClient = %mineSC; + else + %sourceClient = isObject(%sourceObject) ? %sourceObject.getOwnerClient() : 0; + + %targetTeam = %targetClient.team; + + //if the source object is a player object, player's don't have sensor groups + // if it's a turret, get the sensor group of the target + // if its a vehicle (of any type) use the sensor group + if (%sourceClient) + %sourceTeam = %sourceClient.getSensorGroup(); + else if(%damageType == $DamageType::Suicide) + %sourceTeam = 0; + //-------------------------------------------------------------------------------------------------------------------- + // z0dd - ZOD, 4/8/02. Check to see if this turret has a valid owner, if not clear the variable. + else if(isObject(%sourceObject) && %sourceObject.getClassName() $= "Turret") + { + %sourceTeam = getTargetSensorGroup(%sourceObject.getTarget()); + if(%sourceObject.owner !$="" && (%sourceObject.owner.team != %sourceObject.team || !isObject(%sourceObject.owner))) + { + %sourceObject.owner = ""; + } + } + //-------------------------------------------------------------------------------------------------------------------- + else if( isObject(%sourceObject) && + ( %sourceObject.getClassName() $= "FlyingVehicle" || %sourceObject.getClassName() $= "WheeledVehicle" || %sourceObject.getClassName() $= "HoverVehicle")) + %sourceTeam = getTargetSensorGroup(%sourceObject.getTarget()); + else + { + if (isObject(%sourceObject) && %sourceObject.getTarget() >= 0 ) + { + %sourceTeam = getTargetSensorGroup(%sourceObject.getTarget()); + } + else + { + %sourceTeam = -1; + } + } + + // if teamdamage is off, and both parties are on the same team + // (but are not the same person), apply no damage + if(!$teamDamage && (%targetClient != %sourceClient) && (%targetTeam == %sourceTeam)) + return; + + if(%targetObject.isShielded && %damageType != $DamageType::Blaster) + %amount = %data.checkShields(%targetObject, %position, %amount, %damageType); + + if(%amount == 0) + return; + + // Set the damage flash + %damageScale = %data.damageScale[%damageType]; + if(%damageScale !$= "") + %amount *= %damageScale; + + %flash = %targetObject.getDamageFlash() + (%amount * 2); + if (%flash > 0.75) + %flash = 0.75; + + %previousDamage = %targetObject.getDamagePercent(); + %targetObject.setDamageFlash(%flash); + %targetObject.applyDamage(%amount); + Game.onClientDamaged(%targetClient, %sourceClient, %damageType, %sourceObject); + + %targetClient.lastDamagedBy = %damagingClient; + %targetClient.lastDamaged = getSimTime(); + + //now call the "onKilled" function if the client was... you know... + if(%targetObject.getState() $= "Dead") + { + // where did this guy get it? + %damLoc = %targetObject.getDamageLocation(%position); + + // should this guy be blown apart? + if( %damageType == $DamageType::Explosion || + %damageType == $DamageType::TankMortar || + %damageType == $DamageType::Mortar || + %damageType == $DamageType::MortarTurret || + %damageType == $DamageType::BomberBombs || + %damageType == $DamageType::SatchelCharge || + %damageType == $DamageType::Missile ) + { + if( %previousDamage >= 0.35 ) // only if <= 35 percent damage remaining + { + %targetObject.setMomentumVector(%momVec); + %targetObject.blowup(); + } + } + + // this should be funny... + if( %damageType == $DamageType::VehicleSpawn ) + { + %targetObject.setMomentumVector("0 0 1"); + %targetObject.blowup(); + } + + // If we were killed, max out the flash + %targetObject.setDamageFlash(0.75); + + %damLoc = %targetObject.getDamageLocation(%position); + Game.onClientKilled(%targetClient, %sourceClient, %damageType, %sourceObject, %damLoc); + } + else if ( %amount > 0.1 ) + { + if( %targetObject.station $= "" && %targetObject.isCloaked() ) + { + %targetObject.setCloaked( false ); + %targetObject.reCloak = %targetObject.schedule( 500, "setCloaked", true ); + } + + playPain( %targetObject ); + } +} + +function Armor::onImpact(%data, %playerObject, %collidedObject, %vec, %vecLen) +{ + %data.damageObject(%playerObject, 0, VectorAdd(%playerObject.getPosition(),%vec), + %vecLen * %data.speedDamageScale, $DamageType::Ground); +} + +function Armor::applyConcussion( %this, %dist, %radius, %sourceObject, %targetObject ) +{ + %percentage = 1 - ( %dist / %radius ); + %random = getRandom(); + + if( %sourceObject == %targetObject ) + { + %flagChance = 1.0; + %itemChance = 1.0; + } + else + { + %flagChance = 0.7; + %itemChance = 0.7; + } + + %probabilityFlag = %flagChance * %percentage; + %probabilityItem = %itemChance * %percentage; + + if( %random <= %probabilityFlag ) + { + Game.applyConcussion( %targetObject ); + } + + if( %random <= %probabilityItem ) + { + %player = %targetObject; + %numWeapons = 0; + + // blaster 0 + // plasma 1 + // chain 2 + // disc 3 + // grenade 4 + // snipe 5 + // elf 6 + // mortar 7 + + //get our inventory + if( %weaps[0] = %player.getInventory("Blaster") > 0 ) %numWeapons++; + if( %weaps[1] = %player.getInventory("Plasma") > 0 ) %numWeapons++; + if( %weaps[2] = %player.getInventory("Chaingun") > 0 ) %numWeapons++; + if( %weaps[3] = %player.getInventory("Disc") > 0 ) %numWeapons++; + if( %weaps[4] = %player.getInventory("GrenadeLauncher") > 0 ) %numWeapons++; + if( %weaps[5] = %player.getInventory("SniperRifle") > 0 ) %numWeapons++; + if( %weaps[6] = %player.getInventory("ELFGun") > 0 ) %numWeapons++; + if( %weaps[7] = %player.getInventory("Mortar") > 0 ) %numWeapons++; + + %foundWeapon = false; + %attempts = 0; + + if( %numWeapons > 0 ) + { + while( !%foundWeapon ) + { + %rand = mFloor( getRandom() * 8 ); + if( %weaps[ %rand ] ) + { + %foundWeapon = true; + + switch ( %rand ) + { + case 0: + %player.use("Blaster"); + case 1: + %player.use("Plasma"); + case 2: + %player.use("Chaingun"); + case 3: + %player.use("Disc"); + case 4: + %player.use("GrenadeLauncher"); + case 5: + %player.use("SniperRifle"); + case 6: + %player.use("ElfGun"); + case 7: + %player.use("Mortar"); + } + + %image = %player.getMountedImage( $WeaponSlot ); + %player.throw( %image.item ); + %player.client.setWeaponsHudItem( %image.item, 0, 0 ); + %player.throwPack(); + } + else + { + %attempts++; + if( %attempts > 10 ) + %foundWeapon = true; + } + } + } + else + { + %targetObject.throwPack(); + %targetObject.throwWeapon(); + } + } +} + +//---------------------------------------------------------------------------- + +$DeathCry[1] = 'avo.deathCry_01'; +$DeathCry[2] = 'avo.deathCry_02'; +$PainCry[1] = 'avo.grunt'; +$PainCry[2] = 'avo.pain'; + +function playDeathCry( %obj ) +{ + %client = %obj.client; + %random = getRandom(1) + 1; + %desc = AudioClosest3d; + + playTargetAudio( %client.target, $DeathCry[%random], %desc, false ); +} + +function playPain( %obj ) +{ + %client = %obj.client; + %random = getRandom(1) + 1; + %desc = AudioClosest3d; + + playTargetAudio( %client.target, $PainCry[%random], %desc, false); +} + +//---------------------------------------------------------------------------- + +//$DefaultPlayerArmor = LightMaleHumanArmor; +$DefaultPlayerArmor = Light; + +function Player::setArmor(%this,%size) +{ + // Takes size as "Light","Medium", "Heavy" + %client = %this.client; + if (%client.race $= "Bioderm") + // Only have male bioderms. + %armor = %size @ "Male" @ %client.race @ Armor; + else + %armor = %size @ %client.sex @ %client.race @ Armor; + //echo("Player::armor: " @ %armor); + %this.setDataBlock(%armor); + %client.armor = %size; +} + +function getDamagePercent(%maxDmg, %dmgLvl) +{ + return (%dmgLvl / %maxDmg); +} + +function Player::getArmorSize(%this) +{ + // return size as "Light","Medium", "Heavy" + %dataBlock = %this.getDataBlock().getName(); + if (getSubStr(%dataBlock, 0, 5) $= "Light") + return "Light"; + else if (getSubStr(%dataBlock, 0, 6) $= "Medium") + return "Medium"; + else if (getSubStr(%dataBlock, 0, 5) $= "Heavy") + return "Heavy"; + else + return "Unknown"; +} + +function Player::pickup(%this,%obj,%amount) +{ + %data = %obj.getDataBlock(); + // Don't pick up a pack if we already have one mounted + if (%data.className $= Pack && + %this.getMountedImage($BackpackSlot) != 0) + return 0; + // don't pick up a weapon (other than targeting laser) if player's at maxWeapons + else if(%data.className $= Weapon + && %data.getName() !$= "TargetingLaser" // Special case + && %this.weaponCount >= %this.getDatablock().maxWeapons) + return 0; + // don't allow players to throw large packs at pilots (thanks Wizard) + else if(%data.className $= Pack && %data.image.isLarge && %this.isPilot()) + return 0; + return ShapeBase::pickup(%this,%obj,%amount); +} + +function Player::use( %this,%data ) +{ + // If player is in a station then he can't use any items + if(%this.station !$= "") + return false; + + // Convert the word "Backpack" to whatever is in the backpack slot. + if ( %data $= "Backpack" ) + { + if ( %this.inStation ) + return false; + + if ( %this.isPilot() ) + { + messageClient( %this.client, 'MsgCantUsePack', '\c2You can\'t use your pack while piloting.~wfx/misc/misc.error.wav' ); + return( false ); + } + else if ( %this.isWeaponOperator() ) + { + messageClient( %this.client, 'MsgCantUsePack', '\c2You can\'t use your pack while in a weaponry position.~wfx/misc/misc.error.wav' ); + return( false ); + } + + %image = %this.getMountedImage( $BackpackSlot ); + if ( %image ) + %data = %image.item; + } + + // Can't use some items when piloting or your a weapon operator + if ( %this.isPilot() || %this.isWeaponOperator() ) + if ( %data.getName() !$= "RepairKit" ) + return false; + + return ShapeBase::use( %this, %data ); +} + +function Player::maxInventory(%this,%data) +{ + %max = ShapeBase::maxInventory(%this,%data); + if (%this.getInventory(AmmoPack)) + %max += AmmoPack.max[%data.getName()]; + return %max; +} + +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::isWeaponOperator(%this) +{ + %vehicle = %this.getObjectMount(); + if ( %vehicle ) + { + %weaponNode = %vehicle.getDatablock().weaponNode; + if ( %weaponNode > 0 && %vehicle.getMountNodeObject( %weaponNode ) == %this ) + return( true ); + } + + return( false ); +} + +function Player::liquidDamage(%obj, %data, %damageAmount, %damageType) +{ + if(%obj.getState() !$= "Dead") + { + %data.damageObject(%obj, 0, "0 0 0", %damageAmount, %damageType); + %obj.lDamageSchedule = %obj.schedule(50, "liquidDamage", %data, %damageAmount, %damageType); + } + else + %obj.lDamageSchedule = ""; +} + +function Armor::onEnterLiquid(%data, %obj, %coverage, %type) +{ + if(isObject(%obj)) { + if(%obj.client.isAiControlled()) + return; + } + switch(%type) + { + case 0: + //Water + case 1: + //Ocean Water + case 2: + //River Water + case 3: + //Stagnant Water + case 4: + //Lava + %obj.liquidDamage(%data, $DamageLava, $DamageType::Lava); + case 5: + //Hot Lava + %obj.liquidDamage(%data, $DamageHotLava, $DamageType::Lava); + case 6: + //Crusty Lava + %obj.liquidDamage(%data, $DamageCrustyLava, $DamageType::Lava); + case 7: + //Quick Sand + } +} + +function Armor::onLeaveLiquid(%data, %obj, %type) +{ + switch(%type) + { + case 0: + //Water + case 1: + //Ocean Water + case 2: + //River Water + case 3: + //Stagnant Water + case 4: + //Lava + case 5: + //Hot Lava + case 6: + //Crusty Lava + case 7: + //Quick Sand + } + + if(%obj.lDamageSchedule !$= "") + { + cancel(%obj.lDamageSchedule); + %obj.lDamageSchedule = ""; + } +} + +function Armor::onTrigger(%data, %player, %triggerNum, %val) +{ + if (%triggerNum == 4) + { + // Throw grenade + if (%val == 1) + { + %player.grenTimer = 1; + } + else + { + if (%player.grenTimer == 0) + { + // Bad throw for some reason + } + else + { + %player.use(Grenade); + %player.grenTimer = 0; + } + } + } + else if (%triggerNum == 5) + { + // Throw mine + if (%val == 1) + { + %player.mineTimer = 1; + } + else + { + if (%player.mineTimer == 0) + { + // Bad throw for some reason + } + else + { + %player.use(Mine); + %player.mineTimer = 0; + } + } + } + else if (%triggerNum == 3) + { + // val = 1 when jet key (LMB) first pressed down + // val = 0 when jet key released + // MES - do we need this at all any more? + if(%val == 1) + %player.isJetting = true; + else + %player.isJetting = false; + } +} + +function Player::setMoveState(%obj, %move) +{ + %obj.disableMove(%move); +} + +function Armor::onLeaveMissionArea(%data, %obj) +{ + Game.leaveMissionArea(%data, %obj); +} + +function Armor::onEnterMissionArea(%data, %obj) +{ + Game.enterMissionArea(%data, %obj); +} + +function Armor::animationDone(%data, %obj) +{ + if(%obj.animResetWeapon !$= "") + { + if(%obj.getMountedImage($WeaponSlot) == 0) + if(%obj.inv[%obj.lastWeapon]) + %obj.use(%obj.lastWeapon); + %obj.animSetWeapon = ""; + } +} + +function playDeathAnimation(%player, %damageLocation, %type) +{ + %vertPos = firstWord(%damageLocation); + %quadrant = getWord(%damageLocation, 1); + + //echo("vert Pos: " @ %vertPos); + //echo("quad: " @ %quadrant); + + if( %type == $DamageType::Explosion || %type == $DamageType::Mortar || %type == $DamageType::Grenade) + { + if(%quadrant $= "front_left" || %quadrant $= "front_right") + %curDie = $PlayerDeathAnim::ExplosionBlowBack; + else + %curDie = $PlayerDeathAnim::TorsoBackFallForward; + } + else if(%vertPos $= "head") + { + if(%quadrant $= "front_left" || %quadrant $= "front_right" ) + %curDie = $PlayerDeathAnim::HeadFrontDirect; + else + %curDie = $PlayerDeathAnim::HeadBackFallForward; + } + else if(%vertPos $= "torso") + { + if(%quadrant $= "front_left" ) + %curDie = $PlayerDeathAnim::TorsoLeftSpinDeath; + else if(%quadrant $= "front_right") + %curDie = $PlayerDeathAnim::TorsoRightSpinDeath; + else if(%quadrant $= "back_left" ) + %curDie = $PlayerDeathAnim::TorsoBackFallForward; + else if(%quadrant $= "back_right") + %curDie = $PlayerDeathAnim::TorsoBackFallForward; + } + else if (%vertPos $= "legs") + { + if(%quadrant $= "front_left" || %quadrant $= "back_left") + %curDie = $PlayerDeathAnim::LegsLeftGimp; + if(%quadrant $= "front_right" || %quadrant $= "back_right") + %curDie = $PlayerDeathAnim::LegsRightGimp; + } + + if(%curDie $= "" || %curDie < 1 || %curDie > 11) + %curDie = 1; + + %player.setActionThread("Death" @ %curDie); +} + +function Armor::onDamage(%data, %obj) +{ + if(%obj.station !$= "" && %obj.getDamageLevel() == 0) + %obj.station.getDataBlock().endRepairing(%obj.station); +} diff --git a/server.cs b/server.cs new file mode 100644 index 0000000..8e43f4e --- /dev/null +++ b/server.cs @@ -0,0 +1,3187 @@ +$classicVersion = "1.5.2"; // z0dd - ZOD, 5/12/04. Set the version. + +if($Host::TimeLimit $= "") + $Host::TimeLimit = 20; + +$SB::WODec = 0.004; // whiteout +$SB::DFDec = 0.02; // damageFlash + +$Classic::gravSetting = -26.9; // z0dd - ZOD, 9/13/02. Classic Gravity setting +$Classic::cameraSpeed = 50; +$Camera::movementSpeed = $Classic::cameraSpeed; // z0dd - ZOD, 9/13/02. Classic camera speed. + +// z0dd - ZOD, 12/7/02. Put server packet rate settings in ServerPrefs.cs +$pref::Net::PacketRateToClient = $Host::ClassicPacketRateToClient; +$pref::Net::PacketSize = $Host::ClassicPacketSize; +$ConnectCount = 0; // z0dd - ZOD, 7/17/03. Used for connection log. +$BackupPassword = $Host::Password; // z0dd - ZOD, 8/09/03. Backup the server pass + +// ----------------------------------------------------- +// z0dd - ZOD, 6/22/02. Addition. +// Alert players on server that a remote connection has +// been established to the server. +$TelnetSpam = 0; +function onTelnetConnect(%ip, %access) +{ + %level = %access == 1 ? "full" : "read"; + %snd = '~wfx/misc/diagnostic_on.wav'; + %msg = '\c1Remote telnet connection established.%1'; + if($Host::TournamentMode && $TelnetSpam == 0) + { + messageAll('MsgTelnetConnect', %msg, %snd); + logEcho("Incomming telnet connection from: " @ %ip @ " with " @ %level @ " access privledges", 1); + $TelnetSpam = 1; + schedule(2000, 0, "clearTelnetSpam"); + } +} + +function clearTelnetSpam() +{ + $TelnetSpam = 0; +} +// ----------------------------------------------------- + +function VerifyCDCheck(%func) +{ + if (!cdFileCheck()) + messageBoxOkCancel("TRIBES 2 CD CHECK", "You must have the Tribes 2 CD in the CD-ROM drive while playing Tribes 2. Please insert the CD.", "schedule(0, 0, VerifyCDCheck, " @ %func @ ");", "quit();"); + else + call(%func); +} + +function logEcho(%msg, %export) +{ + // z0dd - ZOD, 5/19/03. Changed from $ClassicLogEchoEnabled, allow server owners to modify + if($Host::ClassicLogEchoEnabled) + { + $AdminLog::new = formatTimeString("mm.dd.yy" SPC "h:nn" SPC "A") SPC %msg; + %file = formatTimeString("mm.dd.yy") @ "Admin.log"; + echo("LOG: " @ $AdminLog::new); + if(%export == 1) + export("$AdminLog::*", $Host::ClassicAdminLogPath @"/"@ %file, true); + } +} + +//-------------------------------------------------------------------------- +// z0dd - ZOD, 3/27/02. Auto restart server after specified time + +function AutoRestart() +{ + if(!$Host::TournamentMode) + { + $AutoRestart = 1; + centerPrintAll("SERVER WILL BE AUTO REBOOTING NEXT MISSION.", 5, 1); + messageAll( 'MsgServerRestart', '\c2SERVER WILL BE AUTO REBOOTING NEXT MISSION.~wfx/misc/red_alert.wav'); + logEcho("Automatic server restart on mission end begining."); + } + else + schedule(300000, 0, "AutoRestart"); // Check back in 5 minutes +} +//-------------------------------------------------------------------------- + +function CreateServer(%mission, %missionType) +{ + DestroyServer(); + + // z0dd - ZOD, 3/27/02. Automatically reboot the server after a specified time. + $AutoRestart = 0; // Paranoia + if($Host::ClassicAutoRestartServer == 1) + schedule($Host::ClassicRestartTime * 3600000, 0, "AutoRestart"); + + if($Host::ClassicTelnet) + telnetsetparameters($Host::ClassicTelnetPort, $Host::ClassicTelnetPassword, $Host::ClassicTelnetListenPass); + + // Load server data blocks + exec("scripts/commanderMapIcons.cs"); + exec("scripts/markers.cs"); + exec("scripts/serverAudio.cs"); + exec("scripts/damageTypes.cs"); + exec("scripts/deathMessages.cs"); + exec("scripts/inventory.cs"); + exec("scripts/camera.cs"); + exec("scripts/particleEmitter.cs"); // Must exist before item.cs and explosion.cs + exec("scripts/particleDummies.cs"); + exec("scripts/projectiles.cs"); // Must exits before item.cs + exec("scripts/player.cs"); + exec("scripts/gameBase.cs"); + exec("scripts/staticShape.cs"); + exec("scripts/weapons.cs"); + exec("scripts/turret.cs"); + exec("scripts/weapTurretCode.cs"); + exec("scripts/pack.cs"); + exec("scripts/vehicles/vehicle_spec_fx.cs"); // Must exist before other vehicle files or CRASH BOOM + exec("scripts/vehicles/serverVehicleHud.cs"); + exec("scripts/vehicles/vehicle_shrike.cs"); + exec("scripts/vehicles/vehicle_bomber.cs"); + exec("scripts/vehicles/vehicle_havoc.cs"); + exec("scripts/vehicles/vehicle_wildcat.cs"); + exec("scripts/vehicles/vehicle_tank.cs"); + exec("scripts/vehicles/vehicle_mpb.cs"); + exec("scripts/vehicles/vehicle.cs"); // Must be added after all other vehicle files or EVIL BAD THINGS + exec("scripts/ai.cs"); + exec("scripts/item.cs"); + exec("scripts/station.cs"); + exec("scripts/simGroup.cs"); + exec("scripts/trigger.cs"); + exec("scripts/forceField.cs"); + exec("scripts/lightning.cs"); + exec("scripts/weather.cs"); + exec("scripts/deployables.cs"); + //exec("scripts/stationSetInv.cs"); // z0dd - ZOD, 5/18/03. Not used. + exec("scripts/navGraph.cs"); + exec("scripts/targetManager.cs"); + exec("scripts/serverCommanderMap.cs"); + exec("scripts/environmentals.cs"); + exec("scripts/power.cs"); + exec("scripts/supportClassic.cs"); // z0dd - ZOD, 5/13/02. Execute the support functions. + exec("scripts/practice.cs"); // z0dd - ZOD, 3/13/02. Execute practice mode server functions. + exec("scripts/serverTasks.cs"); + exec("scripts/admin.cs"); + exec("prefs/banlist.cs"); + + // ------------------------------------------------------------------- + // z0dd - ZOD, 9/29/02. Removed T2 demo code from here. + // z0dd - ZOD, 12/8/02. Explicit (base & classic) game type loading. + exec("scripts/defaultGame.cs"); + exec("scripts/CTFGame.cs"); + exec("scripts/SCtFGame.cs"); + exec("scripts/PracticeCTFGame.cs"); + exec("scripts/TeamHuntersGame.cs"); + exec("scripts/SinglePlayerGame.cs"); + exec("scripts/SiegeGame.cs"); + exec("scripts/RabbitGame.cs"); + exec("scripts/HuntersGame.cs"); + exec("scripts/DnDGame.cs"); + exec("scripts/DMGame.cs"); + exec("scripts/CnHGame.cs"); + exec("scripts/BountyGame.cs"); + if($Host::ClassicLoadTR2Gametype) + { + exec("scripts/TR2Game.cs"); + } + + %search = "scripts/*Game.cs"; + for(%file = findFirstFile(%search); %file !$= ""; %file = findNextFile(%search)) + { + %type = fileBase(%file); // get the name of the script + if((%type !$= aiBountyGame) && + (%type !$= BountyGame) && + (%type !$= CnHGame) && + (%type !$= CTFGame) && + (%type !$= defaultGame) && + (%type !$= DMGame) && + (%type !$= DnDGame) && + (%type !$= HuntersGame) && + (%type !$= PracticeCTFGame) && + (%type !$= RabbitGame) && + (%type !$= SCtFGame) && + (%type !$= SiegeGame) && + (%type !$= SinglePlayerGame) && + (%type !$= TeamHuntersGame) && + (%type !$= TR2Game)) + { + exec("scripts/" @ %type @ ".cs"); + } + } + // ------------------------------------------------------------------- + + // z0dd - ZOD, 9/29/02. Removed T2 demo code from here + + $missionSequence = 0; + $CurrentMissionType = %missionType; + $HostGameBotCount = 0; + $HostGamePlayerCount = 0; + if ( $HostGameType !$= "SinglePlayer" ) + allowConnections(true); + + $ServerGroup = new SimGroup (ServerGroup); + if(%mission $= "") + { + %mission = $HostMissionFile[$HostMission[0,0]]; + %missionType = $HostTypeName[0]; + } + + if ( ( $HostGameType $= "Online" && $pref::Net::DisplayOnMaster !$= "Never" ) ) // z0dd - ZOD, 9/29/02. Removed T2 demo code from here + schedule(0,0,startHeartbeat); + + // setup the bots for this server + if( $Host::BotsEnabled ) // z0dd - ZOD, 9/29/02. Removed T2 demo code from here + initGameBots( %mission, %missionType ); + + // z0dd - ZOD, 9/13/02. For TR2 compatability + // This is a failsafe way of ensuring that default gravity is always restored + // if a game type (such as TR2) changes it. It is placed here so that listen + // servers will work after opening and closing different gametypes. + $DefaultGravity = getGravity(); + + // z0dd - ZOD, 07/19/03. fix for stuttering dual processor servers. + // Moved here because it seems server must be created first. + if(($Host::ClassicUseHighPerformanceCounter $= "0") || ($Host::ClassicUseHighPerformanceCounter == 0)) + { + setPerfCounterEnable(0); + } + else + { + setPerfCounterEnable(1); + } + + // load the mission... + loadMission(%mission, %missionType, true); + +} + +function initGameBots( %mission, %mType ) +{ + echo( "adding bots..." ); + + AISystemEnabled( false ); + if ( $Host::BotCount > 0 && %mType !$= "SinglePlayer" ) + { + // Make sure this mission is bot enabled: + for ( %idx = 0; %idx < $HostMissionCount; %idx++ ) + { + if ( $HostMissionFile[%idx] $= %mission ) + break; + } + + if ( $BotEnabled[%idx] ) + { + if ( $Host::BotCount > 32 ) + $HostGameBotCount = 32; + else + $HostGameBotCount = $Host::BotCount; + + if ( $Host::BotCount > $Host::MaxPlayers - 1 ) + $HostGameBotCount = $Host::MaxPlayers - 1; + + //set the objective reassessment timeslice var + $AITimeSliceReassess = 0; + aiConnectMultiple( $HostGameBotCount, $Host::MinBotDifficulty, $Host::MaxBotDifficulty, -1 ); + } + else + { + $HostGameBotCount = 0; + } + } +} + +function getValidMap(%misType) +{ + // Find the index of the mission type and choose a random map of this type + for ( %type = 0; %type < $HostTypeCount; %type++ ) + { + if ( $HostTypeName[%type] $= %misType ) + break; + } + + // Now get the mission count of this type + for(%i = 0; $HostMission[%type, %i] !$=""; %i++) + %count = %i; + + if ( $HostGameBotCount > 0 ) + { + // Pick a mission from this type within its mission count + for(%j = 0; $HostMissionFile[$HostMission[%type, %j]] !$= ""; %j++) + { + %file = $HostMissionFile[$HostMission[%type, %j]]; + for ( %mis = 0; %mis < $HostMissionCount; %mis++ ) + { + if( $HostMissionFile[%mis] $= %file ) + { + if($BotEnabled[%mis]) + { + %file = $HostMissionFile[$HostMission[%type, %j]]; + break; + } + } + } + } + } + else + %file = $HostMissionFile[$HostMission[%type, mFloor(getRandom(0, %count-1))]]; + + return %file; +} + +function findNextCycleMission(%type) +{ + %numPlayers = ClientGroup.getCount(); + if($Host::ClassicCycleMisTypes || $Host::ClassicRandomMisTypes) + { + %tempMission = getValidMap(%type); + if(%tempMission $= "") + { + %tempMission = $CurrentMission; + %type = $CurrentMissionType; + } + } + else + %tempMission = $CurrentMission; + + %failsafe = 0; + while (1) + { + %nextMissionIndex = getNextMission(%tempMission, %type); + %nextPotentialMission = $HostMissionFile[%nextMissionIndex]; + + //just cycle to the next if we've gone all the way around... + if (%nextPotentialMission $= $CurrentMission || %failsafe >= 1000) + { + %nextMissionIndex = getNextMission($CurrentMission, %type); + //return $HostMissionName[%nextMissionIndex]; // z0dd - ZOD - Founder, 10/06/02. Was trying to load a mission name instead of file. + return $HostMissionFile[%nextMissionIndex]; + } + + //get the player count limits for this mission + %limits = $Host::MapPlayerLimits[%nextPotentialMission, %type]; + if (%limits $= "") + return %nextPotentialMission; + else + { + %minPlayers = getWord(%limits, 0); + %maxPlayers = getWord(%limits, 1); + + if ((%minPlayers < 0 || %minPlayers == -1 || %numPlayers >= %minPlayers) && (%maxPlayers < 0 || %maxPlayers == -1 || %numPlayers <= %maxPlayers)) + return %nextPotentialMission; + } + + //since we didn't return the mission, we must not have an acceptable number of players - check the next + %tempMission = %nextPotentialMission; + %failsafe++; + } +} + +function CycleMissions() +{ + echo( "cycling mission. " @ ClientGroup.getCount() @ " clients in game." ); + if($Host::ClassicCycleMisTypes && !$Host::ClassicRandomMisTypes) + { + if($HostTypeName[$HostTypeCount + 1] !$= "") + %type = $HostTypeName[$HostTypeCount + 1]; + else + %type = $HostTypeName[0]; + + %nextMission = findNextCycleMission(%type); + messageAll( 'MsgClient', 'Loading %1 (%2)...', %nextMission, $HostTypeDisplayName[%type] ); + loadMission( %nextMission, %type ); + } + else if($Host::ClassicRandomMisTypes && !$Host::ClassicCycleMisTypes) + { + %ran = mFloor(getRandom(0, $HostTypeCount)); + if($HostTypeName[%ran] !$= "") + %type = $HostTypeName[%ran]; + else + %type = $HostTypeName[0]; + + %nextMission = findNextCycleMission(%type); + messageAll( 'MsgClient', 'Loading %1 (%2)...', %nextMission, $HostTypeDisplayName[%type] ); + loadMission( %nextMission, %type ); + } + else + { + %nextMission = findNextCycleMission($CurrentMissionType); + if(%nextMission $= "") // z0dd - ZOD, 5/17/03. Make sure it's returning a mission, otherwise, repeat. + %nextMission = $CurrentMission; + + messageAll( 'MsgClient', 'Loading %1 (%2)...', %nextMission, $MissionTypeDisplayName ); + loadMission( %nextMission, $CurrentMissionType ); + } +} + +function DestroyServer() +{ + $missionRunning = false; + allowConnections(false); + stopHeartbeat(); + if ( isObject( MissionGroup ) ) + MissionGroup.delete(); + if ( isObject( MissionCleanup ) ) + MissionCleanup.delete(); + if(isObject(game)) + { + game.deactivatePackages(); + game.delete(); + } + if(isObject($ServerGroup)) + $ServerGroup.delete(); + + // delete all the connections: + while(ClientGroup.getCount()) + { + %client = ClientGroup.getObject(0); + if (%client.isAIControlled()) + %client.drop(); + else + %client.delete(); + } + + // delete all the data blocks... + // this will cause problems if there are any connections + deleteDataBlocks(); + + // reset the target manager + resetTargetManager(); + + echo( "exporting server prefs..." ); + export( "$Host::*", $serverprefs, false ); + purgeResources(); + + // z0dd - ZOD, 9/13/02. For TR2 compatability. + // This is a failsafe way of ensuring that default gravity is always restored + // if a game type (such as TR2) changes it. It is placed here so that listen + // servers will work after opening and closing different gametypes. + if ($DefaultGravity !$= "") + setGravity($DefaultGravity); +} + +function Disconnect() +{ + if ( isObject( ServerConnection ) ) + ServerConnection.delete(); + DisconnectedCleanup(); + DestroyServer(); +} + +function DisconnectedCleanup() +{ + $CurrentMissionType = ""; + $CurrentMission = ""; + + // Make sure we're not still waiting for the loading info: + cancelLoadInfoCheck(); + + // clear the chat hud message vector + HudMessageVector.clear(); + if ( isObject( PlayerListGroup ) ) + PlayerListGroup.delete(); + + // terminate all playing sounds + alxStopAll(); + + // clean up voting + voteHud.voting = false; + mainVoteHud.setvisible(0); + + // clear all print messages + clientCmdclearBottomPrint(); + clientCmdClearCenterPrint(); + + // clear the inventory and weapons hud + weaponsHud.clearAll(); + inventoryHud.clearAll(); + + // back to the launch screen + Canvas.setContent(LaunchGui); + if ( isObject( MusicPlayer ) ) + MusicPlayer.stop(); + clearTextureHolds(); + purgeResources(); + + if ( $PlayingOnline ) + { + // Restart the email check: + if ( !EmailGui.checkingEmail && EmailGui.checkSchedule $= "" ) + CheckEmail( true ); + + IRCClient::onLeaveGame(); + } +} + +// we pass the guid as well, in case this guy leaves the server. +function kick( %client, %admin, %guid ) +{ + if(%admin) + messageAll( 'MsgAdminForce', '\c2%1 has kicked %2.', %admin.name, %client.name ); // z0dd - ZOD, 7/13/03. Tell who kicked + else + messageAll( 'MsgVotePassed', '\c2%1 was kicked by vote.', Game.kickClientName ); + + messageClient(%client, 'onClientKicked', ""); + messageAllExcept( %client, -1, 'MsgClientDrop', "", Game.kickClientName, %client ); + + if( %client.isAIControlled() ) + { + if($Host::ClassicCanKickBots || %admin.isAdmin) + { + if(!$Host::ClassicBalancedBots) + { + $HostGameBotCount--; + %client.drop(); + } + } + } + else + { + if( $playingOnline ) // won games + { + %count = ClientGroup.getCount(); + %found = false; + for( %i = 0; %i < %count; %i++ ) // see if this guy is still here... + { + %cl = ClientGroup.getObject( %i ); + if( %cl.guid == %guid ) + { + %found = true; + + // kill and delete this client, their done in this server. + if( isObject( %cl.player ) ) + %cl.player.scriptKill(0); + + if ( isObject( %cl ) ) + { + %cl.setDisconnectReason( %admin.nameBase @ " has kicked you out of the game." ); // z0dd - ZOD, 7/13/03. Tell who kicked + %cl.schedule(700, "delete"); + } + BanList::add( %guid, "0", $Host::KickBanTime ); + } + } + if( !%found ) + BanList::add( %guid, "0", $Host::KickBanTime ); // keep this guy out for a while since he left. + } + else // lan games + { + // kill and delete this client + if( isObject( %client.player ) ) + %client.player.scriptKill(0); + + if ( isObject( %client ) ) + { + %client.setDisconnectReason( "You have been kicked out of the game." ); + %client.schedule(700, "delete"); + } + BanList::add( 0, %client.getAddress(), $Host::KickBanTime ); + } + } +} + +function ban( %client, %admin ) +{ + if ( %admin ) + messageAll('MsgAdminForce', '\c2%1 has banned %2.', %admin.name, %client.name); // z0dd - ZOD, 10/03/2. Tell who banned + else + messageAll( 'MsgVotePassed', '\c2%1 was banned by vote.', %client.name ); + + messageClient(%client, 'onClientBanned', ""); + messageAllExcept( %client, -1, 'MsgClientDrop', "", %client.name, %client ); + + // kill and delete this client + if( isObject(%client.player) ) + %client.player.scriptKill(0); + + if ( isObject( %client ) ) + { + %client.setDisconnectReason( %admin.nameBase @ " has banned you from this server." ); // z0dd - ZOD, 10/03/2. Tell who banned + %client.schedule(700, "delete"); + } + BanList::add(%client.guid, %client.getAddress(), $Host::BanTime); +} + +function getValidVoicePitch(%voice, %voicePitch) +{ + if (%voicePitch < -1.0) + %voicePitch = -1.0; + else if (%voicePitch > 1.0) + %voicePitch = 1.0; + + //Voice pitch range is from 0.5 to 2.0, however, we should tighten the range to + //avoid players sounding like mickey mouse, etc... + //see if we're pitching down - clamp the min pitch at 0.875 + if (%voicePitch < 0) + return (1.0 + (0.125 * %voicePitch)); + + //max voice pitch is 1.125 + else if (%voicePitch > 0) + return 1.0 + (0.125 * %voicePitch); + + else + return 1.0; +} + +// z0dd - ZOD, 9/29/02. Removed T2 demo code from here + +function GameConnection::onConnect( %client, %name, %raceGender, %skin, %voice, %voicePitch ) +{ + %client.setMissionCRC($missionCRC); + sendLoadInfoToClient( %client ); + + //%client.setSimulatedNetParams(0.1, 30); + + // z0dd - ZOD, 9/29/02. Removed T2 demo code from here + + // --------------------------------------------------- + // z0dd - ZOD, 9/29/02. Removed T2 demo code from here + + // if hosting this server, set this client to superAdmin + if(%client.getAddress() $= "Local") + { + %client.isAdmin = true; + %client.isSuperAdmin = true; + } + // Get the client's unique id: + %authInfo = %client.getAuthInfo(); + %client.guid = getField( %authInfo, 3 ); + + // check admin and super admin list, and set status accordingly + if ( !%client.isSuperAdmin ) + { + if ( isOnSuperAdminList( %client ) ) + { + %client.isAdmin = true; + %client.isSuperAdmin = true; + } + else if( isOnAdminList( %client ) ) + { + %client.isAdmin = true; + } + } + + // Sex/Race defaults + switch$ ( %raceGender ) + { + case "Human Male": + %client.sex = "Male"; + %client.race = "Human"; + case "Human Female": + %client.sex = "Female"; + %client.race = "Human"; + case "Bioderm": + %client.sex = "Male"; + %client.race = "Bioderm"; + default: + error("Invalid race/gender combo passed: " @ %raceGender); + %client.sex = "Male"; + %client.race = "Human"; + } + %client.armor = "Light"; + + // Override the connect name if this server does not allow smurfs: + %realName = getField( %authInfo, 0 ); + if ( $PlayingOnline && $Host::NoSmurfs ) + %name = %realName; + + if ( strcmp( %name, %realName ) == 0 ) + { + %client.isSmurf = false; + + //make sure the name is unique - that a smurf isn't using this name... + %dup = -1; + %count = ClientGroup.getCount(); + for (%i = 0; %i < %count; %i++) + { + %test = ClientGroup.getObject( %i ); + if (%test != %client) + { + %rawName = stripChars( detag( getTaggedString( %test.name ) ), "\cp\co\c6\c7\c8\c9" ); + if (%realName $= %rawName) + { + %dup = %test; + %dupName = %rawName; + break; + } + } + } + + //see if we found a duplicate name + if (isObject(%dup)) + { + //change the name of the dup + %isUnique = false; + %suffixCount = 1; + while (!%isUnique) + { + %found = false; + %testName = %dupName @ "." @ %suffixCount; + for (%i = 0; %i < %count; %i++) + { + %cl = ClientGroup.getObject(%i); + %rawName = stripChars( detag( getTaggedString( %cl.name ) ), "\cp\co\c6\c7\c8\c9" ); + if (%rawName $= %testName) + { + %found = true; + break; + } + } + + if (%found) + %suffixCount++; + else + %isUnique = true; + } + + //%testName will now have the new unique name... + %oldName = %dupName; + %newName = %testName; + + //MessageAll( 'MsgSmurfDupName', '\c2The real \"%1\" has joined the server.', %dupName ); + //MessageAll( 'MsgClientNameChanged', '\c2The smurf \"%1\" is now called \"%2\".', %oldName, %newName, %dup ); + + %dup.name = addTaggedString(%newName); + setTargetName(%dup.target, %dup.name); + } + + // Add the tribal tag: + %tag = getField( %authInfo, 1 ); + %append = getField( %authInfo, 2 ); + if ( %append ) + %name = "\cp\c6" @ %name @ "\c7" @ %tag @ "\co"; + else + %name = "\cp\c7" @ %tag @ "\c6" @ %name @ "\co"; + + %client.sendGuid = %client.guid; + } + else + { + %client.isSmurf = true; + %client.sendGuid = 0; + %name = stripTrailingSpaces( strToPlayerName( %name ) ); + if ( strlen( %name ) < 3 ) + %name = "Poser"; + + // Make sure the alias is unique: + %isUnique = true; + %count = ClientGroup.getCount(); + for ( %i = 0; %i < %count; %i++ ) + { + %test = ClientGroup.getObject( %i ); + %rawName = stripChars( detag( getTaggedString( %test.name ) ), "\cp\co\c6\c7\c8\c9" ); + if ( strcmp( %name, %rawName ) == 0 ) + { + %isUnique = false; + break; + } + } + + // Append a number to make the alias unique: + if ( !%isUnique ) + { + %suffix = 1; + while ( !%isUnique ) + { + %nameTry = %name @ "." @ %suffix; + %isUnique = true; + + %count = ClientGroup.getCount(); + for ( %i = 0; %i < %count; %i++ ) + { + %test = ClientGroup.getObject( %i ); + %rawName = stripChars( detag( getTaggedString( %test.name ) ), "\cp\co\c6\c7\c8\c9" ); + if ( strcmp( %nameTry, %rawName ) == 0 ) + { + %isUnique = false; + break; + } + } + + %suffix++; + } + + // Success! + %name = %nameTry; + } + + %smurfName = %name; + // Tag the name with the "smurf" color: + %name = "\cp\c8" @ %name @ "\co"; + } + + %client.name = addTaggedString(%name); + if(%client.isSmurf) + %client.nameBase = %smurfName; + else + %client.nameBase = %realName; + + // Make sure that the connecting client is not trying to use a bot skin: + %temp = detag( %skin ); + if ( %temp $= "basebot" || %temp $= "basebbot" ) + %client.skin = addTaggedString( "base" ); + else + %client.skin = addTaggedString( %skin ); + + %client.voice = %voice; + %client.voiceTag = addtaggedString(%voice); + + //set the voice pitch based on a lookup table from their chosen voice + %client.voicePitch = getValidVoicePitch(%voice, %voicePitch); + // z0dd - ZOD, 9/29/02. Removed T2 demo code from here + // --------------------------------------------------- + + %client.justConnected = true; + %client.isReady = false; + + // full reset of client target manager + clientResetTargets(%client, false); + + %client.target = allocClientTarget(%client, %client.name, %client.skin, %client.voiceTag, '_ClientConnection', 0, 0, %client.voicePitch); + %client.score = 0; + %client.team = 0; + + $instantGroup = ServerGroup; + $instantGroup = MissionCleanup; + + echo("CADD: " @ %client @ " " @ %client.getAddress()); + + %count = ClientGroup.getCount(); + for(%cl = 0; %cl < %count; %cl++) + { + %recipient = ClientGroup.getObject(%cl); + if((%recipient != %client)) + { + // These should be "silent" versions of these messages... + messageClient(%client, 'MsgClientJoin', "", + %recipient.name, + %recipient, + %recipient.target, + %recipient.isAIControlled(), + %recipient.isAdmin, + %recipient.isSuperAdmin, + %recipient.isSmurf, + %recipient.sendGuid); + + messageClient(%client, 'MsgClientJoinTeam', "", %recipient.name, $teamName[%recipient.team], %recipient, %recipient.team ); + } + } + +// commandToClient(%client, 'getManagerID', %client); + + commandToClient(%client, 'setBeaconNames', "Target Beacon", "Marker Beacon", "Bomb Target"); + + if ( $CurrentMissionType !$= "SinglePlayer" ) + { + // z0dd - ZOD, 5/08/04. Send message of any gameplay changes +// messageClient( %client, 'MsgClassic', 'Classic \c2Sniper Mod: \c3%1.', ($Host::ClassicLoadSniperChanges ? 'Enabled' : 'Disabled') ); +// messageClient( %client, 'MsgClassic', 'Classic \c2Missile Mod: \c3%1.', ($Host::ClassicLoadMissileChanges ? 'Enabled' : 'Disabled') ); +// messageClient( %client, 'MsgClassic', 'Classic \c2Mortar Mod: \c3%1.', ($Host::ClassicLoadMortarChanges ? 'Enabled' : 'Disabled') ); +// messageClient( %client, 'MsgClassic', 'Classic \c2Blaster Mod: \c3%1.', ($Host::ClassicLoadBlasterChanges ? 'Enabled' : 'Disabled') ); +// messageClient( %client, 'MsgClassic', 'Classic \c2Plasma Turret Mod: \c3%1.', ($Host::ClassicLoadPlasmaTurretChanges ? 'Enabled' : 'Disabled') ); +// messageClient( %client, 'MsgClassic', 'Classic \c2Player Mod: \c3%1.', ($Host::ClassicLoadPlayerChanges ? 'Enabled' : 'Disabled') ); +// messageClient( %client, 'MsgClassic', 'Classic \c2Havoc Mod: \c3%1.', ($Host::ClassicLoadHavocChanges ? 'Enabled' : 'Disabled') ); +// messageClient( %client, 'MsgClassic', 'Classic \c2Mine Mod: \c3%1.', ($Host::ClassicLoadMineChanges ? 'Enabled' : 'Disabled') ); +// messageClient( %client, 'MsgClassic', 'Classic \c2V-Ramming Mod: \c3%1.', ($Host::ClassicLoadVRamChanges ? 'Enabled' : 'Disabled') ); + + // z0dd - ZOD, 9/29/02. Removed T2 demo code from here + messageClient(%client, 'MsgClientJoin', 'Welcome to Tribes2 %1.', + %client.name, + %client, + %client.target, + false, // isBot + %client.isAdmin, + %client.isSuperAdmin, + %client.isSmurf, + %client.sendGuid ); + // z0dd - ZOD, 9/29/02. Removed T2 demo code from here + + messageAllExcept(%client, -1, 'MsgClientJoin', '\c1%1 joined the game.', + %client.name, + %client, + %client.target, + false, // isBot + %client.isAdmin, + %client.isSuperAdmin, + %client.isSmurf, + %client.sendGuid ); + } + else + messageClient(%client, 'MsgClientJoin', "\c0Mission Insertion complete...", + %client.name, + %client, + %client.target, + false, // isBot + false, // isAdmin + false, // isSuperAdmin + false, // isSmurf + %client.sendGuid ); + + //Game.missionStart(%client); + setDefaultInventory(%client); + + if($missionRunning) + %client.startMission(); + $HostGamePlayerCount++; + // z0dd - ZOD, 9/29/02. Removed T2 demo code from here + + if( $Host::ClassicConnectLog ) + { + // z0dd - ZOD, 5/07/04. New logging method based on AurLogging by Aureole + %file = $Host::ClassicConnLogPath @"/"@ formatTimeString("mm.dd.yy") @ "Connect.csv"; + %conn = new FileObject(); + %conn.openForAppend(%file); + %conn.writeLine("\"" @ formatTimeString("mm.dd.yy - h:nn:ss A") @ "\"," @ %client.nameBase @ "\"," @ %client.guid @ "," @ getSubStr(%client.getAddress(), 3, strlen(%client.getAddress()))); + %conn.close(); + %conn.delete(); + echo( "exporting client info to connect.csv..." ); + + // z0dd - ZOD - Founder, 5/25/03. Connect log + //$conn::new[$ConnectCount++] = "Player: " @ %client.nameBase @ " Real Name: " @ %realName @ " Guid: " @ %client.guid @ " Connected from: " @ %client.getAddress(); + //%file = formatTimeString("mm.dd.yy") @ "Connect.log"; + //export("$conn::*", $Host::ClassicConnLogPath @"/"@ %file, true); + } + + // z0dd - ZOD 4/29/02. Activate the clients Classic Huds + // and start off with 0 SAD access attempts. + %client.SadAttempts = 0; + messageClient(%client, 'MsgBomberPilotHud', ""); // Activate the bomber pilot hud + + // z0dd - ZOD, 8/10/02. Get player hit sounds etc. + commandToClient(%client, 'GetClassicModSettings', 1); + + //--------------------------------------------------------- + // z0dd - ZOD, 7/12/02. New AutoPW server function. Sets + // server join password when server reaches x player count. + if($Host::ClassicAutoPWEnabled) + { + if(($Host::ClassicAutoPWPlayerCount != 0 && $Host::ClassicAutoPWPlayerCount !$= "") && ($HostGamePlayerCount >= $Host::ClassicAutoPWPlayerCount)) + AutoPWServer(1); + } + // z0dd - ZOD, 5/12/04. Kick a bot for every client join if balanced bots are set + if( $Host::BotsEnabled ) + { + if($Host::ClassicBalancedBots) + { + for(%i = 0; %i < ClientGroup.getCount(); %i++) + { + %cl = ClientGroup.getObject(%i); + if(%cl.isAIControlled()) + { + %kick = %cl; + break; + } + } + if(%kick !$= "") + { + $HostGameBotCount--; + %kick.drop(); + } + } + } +} + +function GameConnection::onDrop(%client, %reason) +{ + if(isObject(Game)) + Game.onClientLeaveGame(%client); + + // make sure that tagged string of player name is not used + if ( $CurrentMissionType $= "SinglePlayer" ) + messageAllExcept(%client, -1, 'MsgClientDrop', "", getTaggedString(%client.name), %client); + else + messageAllExcept(%client, -1, 'MsgClientDrop', '\c1%1 has left the game.', getTaggedString(%client.name), %client); + + if ( isObject( %client.camera ) ) + %client.camera.delete(); + + // z0dd - ZOD, 6/19/02. Strip the hit sound tags + removeTaggedString(%client.playerHitWav); + removeTaggedString(%client.vehicleHitWav); + + removeTaggedString(%client.name); + removeTaggedString(%client.voiceTag); + removeTaggedString(%client.skin); + freeClientTarget(%client); + + echo("CDROP: " @ %client @ " " @ %client.getAddress()); + $HostGamePlayerCount--; + + // z0dd - ZOD, 5/05/04. Add a bot for every client drop if balanced bots are set + if( $Host::BotsEnabled ) + { + if($Host::ClassicBalancedBots) + { + if(!%client.isAIControlled()) + { + if (serverCanAddBot()) + { + aiConnectMultiple( 1, $Host::MinBotDifficulty, $Host::MaxBotDifficulty, -1 ); + $HostGameBotCount++; + } + } + } + } + + //--------------------------------------------------------- + // z0dd - ZOD, 7/12/02. New AutoPW server function. Sets + // server join password when server reaches x player count. + if($Host::ClassicAutoPWEnabled) + { + if($HostGamePlayerCount < $Host::ClassicAutoPWPlayerCount) + AutoPWServer(0); + } + // reset the server if everyone has left the game + //if( $HostGamePlayerCount - $HostGameBotCount == 0 && $Host::Dedicated && !$resettingServer && !$LoadingMission ) + // schedule(0, 0, "resetServerDefaults"); + + // ------------------------------------------------------------------------------------------------------------ + // z0dd - ZOD, 5/12/02. Reset the server if everyone has left the game and set this mission as startup mission. + // This helps with $Host::ClassicRandomMissions to keep the random more random. + if( $HostGamePlayerCount - $HostGameBotCount == 0 && $Host::Dedicated && !$resettingServer && !$LoadingMission ) + { + $Host::Map = $CurrentMission; + export("$Host::*", $serverprefs, false); + $Host::MissionType = $CurrentMissionType; + export("$Host::*", $serverprefs, false); + schedule(10, 0, "resetServerDefaults"); + } + // ------------------------------------------------------------------------------------------------------------ + +} + +function dismountPlayers() +{ + // make sure all palyers are dismounted from vehicles and have normal huds + %count = ClientGroup.getCount(); + for(%cl = 0; %cl < %count; %cl++) + { + %client = ClientGroup.getObject(%cl); + %player = %client.player; + if(%player.isMounted()) { + %player.unmount(); + commandToClient(%client, 'setHudMode', 'Standard', "", 0); + } + } +} + +function loadMission( %missionName, %missionType, %firstMission ) +{ + if ($AutoRestart) // z0dd - ZOD, 3/26/02. Auto restart server after a specified time. + { + $AutoRestart = 0; + messageAll( 'MsgServerRestart', '\c2SERVER IS AUTO REBOOTING! COME BACK IN 5 MINUTES.~wfx/misc/red_alert.wav'); + logEcho("Auto server restart commencing."); + //schedule(10000, 0, "CreateServer", %missionName, %missionType); // this wasn't working as a cure for servers with NULLs + schedule(10000, 0, quit ); + } + + // z0dd - ZOD, 9/29/02. Removed T2 demo code from here + + // z0dd - ZOD, 9/13/02. TR2 needs this. + if( %missionType $= "TR2" ) + { + $_Camera::movementSpeed = $Camera::movementSpeed; + $Camera::movementSpeed = 80; + } + else + { + %val = ($_Camera::movementSpeed $= "") ? $Classic::cameraSpeed : $_Camera::movementSpeed; // z0dd - ZOD, 9/13/02. Classic camera speed. + $Camera::movementSpeed = %val; + } + + $LoadingMission = true; + disableCyclingConnections(true); + if (!$pref::NoClearConsole) + cls(); + if ( isObject( LoadingGui ) ) + LoadingGui.gotLoadInfo = ""; + buildLoadInfo( %missionName, %missionType ); + + // reset all of these + ClearCenterPrintAll(); + ClearBottomPrintAll(); + + if( $Host::TournamentMode ) // z0dd - ZOD, 9/29/02. Removed T2 demo code from here + resetTournamentPlayers(); + + // Send load info to all the connected clients: + %count = ClientGroup.getCount(); + for ( %cl = 0; %cl < %count; %cl++ ) + { + %client = ClientGroup.getObject( %cl ); + if ( !%client.isAIControlled() ) + sendLoadInfoToClient( %client ); + } + + // allow load condition to exit out + schedule(0,ServerGroup,loadMissionStage1,%missionName,%missionType,%firstMission); +} + +function loadMissionStage1(%missionName, %missionType, %firstMission) +{ + // if a mission group was there, delete prior mission stuff + if(isObject(MissionGroup)) + { + // clear out the previous mission paths + for(%clientIndex = 0; %clientIndex < ClientGroup.getCount(); %clientIndex++) + { + // clear ghosts and paths from all clients + %cl = ClientGroup.getObject(%clientIndex); + %cl.resetGhosting(); + %cl.clearPaths(); + %cl.isReady = ""; + %cl.matchStartReady = false; + } + Game.endMission(); + $lastMissionTeamCount = Game.numTeams; + + MissionGroup.delete(); + MissionCleanup.delete(); + Game.deactivatePackages(); + Game.delete(); + $ServerGroup.delete(); + $ServerGroup = new SimGroup(ServerGroup); + } + + $CurrentMission = %missionName; + $CurrentMissionType = %missionType; + + createInvBanCount(); + echo("LOADING MISSION: " @ %missionName); + + // increment the mission sequence (used for ghost sequencing) + $missionSequence++; + + // if this isn't the first mission, allow some time for the server + // to transmit information to the clients: + +// jff: $currentMission already being used for this purpose, used in 'finishLoadMission' + $MissionName = %missionName; + $missionRunning = false; + + if(!%firstMission) + schedule(15000, ServerGroup, loadMissionStage2); + else + loadMissionStage2(); +} + +function loadMissionStage2() +{ + // create the mission group off the ServerGroup + echo("Stage 2 load"); + $instantGroup = ServerGroup; + + new SimGroup (MissionCleanup); + + if($CurrentMissionType $= "") + { + new ScriptObject(Game) { + class = DefaultGame; + }; + } + else + { + new ScriptObject(Game) { + class = $CurrentMissionType @ "Game"; + superClass = DefaultGame; + }; + } + // allow the game to activate any packages. + Game.activatePackages(); + + // reset the target manager + resetTargetManager(); + + %file = "missions/" @ $missionName @ ".mis"; + if(!isFile(%file)) + return; + + // send the mission file crc to the clients (used for mission lighting) + $missionCRC = getFileCRC(%file); + %count = ClientGroup.getCount(); + for(%i = 0; %i < %count; %i++) + { + %client = ClientGroup.getObject(%i); + if(!%client.isAIControlled()) + %client.setMissionCRC($missionCRC); + } + + $countDownStarted = false; + exec(%file); + $instantGroup = MissionCleanup; + + // pre-game mission stuff + if(!isObject(MissionGroup)) + { + error("No 'MissionGroup' found in mission \"" @ $missionName @ "\"."); + schedule(3000, ServerGroup, CycleMissions); + return; + } + + MissionGroup.cleanNonType($CurrentMissionType); + + // construct paths + pathOnMissionLoadDone(); + + $ReadyCount = 0; + $MatchStarted = false; + $CountdownStarted = false; + AISystemEnabled( false ); + + // Set the team damage here so that the game type can override it: + + // z0dd - ZOD, 9/29/02. Removed T2 demo code from here + if ( $Host::TournamentMode ) + $TeamDamage = 1; + else + $TeamDamage = $Host::TeamDamageOn; + + // z0dd - ZOD, 5/23/03. Setup the defaults + $RandomTeams = $Host::ClassicRandomizeTeams; + $FairTeams = $Host::ClassicFairTeams; + $LimitArmors = $Host::ClassicLimitArmors; + + // z0dd - ZOD 5/27/03. Setup armor max counts + countArmorAllowed(); + + // z0dd - ZOD, 9/29/02. Removed T2 demo code from here + + // z0dd - ZOD, 8/4/02. Gravity change + if(getGravity() !$= $Classic::gravSetting) + setGravity($Classic::gravSetting); + + // z0dd - ZOD, 5/17/03. Set a minimum flight ceiling for all maps. + %area = nameToID("MissionGroup/MissionArea"); + if(%area.flightCeiling < 450) + %area.flightCeiling = 450; + + Game.missionLoadDone(); + + // start all the clients in the mission + $missionRunning = true; + for(%clientIndex = 0; %clientIndex < ClientGroup.getCount(); %clientIndex++) + ClientGroup.getObject(%clientIndex).startMission(); + + if(!$MatchStarted && $LaunchMode !$= "NavBuild" && $LaunchMode !$= "SpnBuild" ) + { + if( $Host::TournamentMode ) // z0dd - ZOD, 9/29/02. Removed T2 demo code from here + checkTourneyMatchStart(); + else if( $currentMissionType !$= "SinglePlayer" ) + checkMissionStart(); + } + + // offline graph builder... + if( $LaunchMode $= "NavBuild" ) + buildNavigationGraph( "Nav" ); + + if( $LaunchMode $= "SpnBuild" ) + buildNavigationGraph( "Spn" ); + + purgeResources(); + disableCyclingConnections(false); + $LoadingMission = false; +} + +function ShapeBase::cleanNonType(%this, %type) +{ + if(%this.missionTypesList $= "") + return; + + for(%i = 0; (%typei = getWord(%this.missionTypesList, %i)) !$= ""; %i++) + if(%typei $= %type) + return; + + // first 32 targets are team targets (never allocated/freed) + // - must reallocate the target if unhiding + if(%this.getTarget() >= 32) + { + freeTarget(%this.getTarget()); + %this.setTarget(-1); + } + if(isObject(%this.trigger)) // z0dd - ZOD, 8/10/02. Clean them triggers too! + %this.trigger.delete(); + + %this.hide(true); +} + +function SimObject::cleanNonType(%this, %type) +{ +} + +function SimGroup::cleanNonType(%this, %type) +{ + for (%i = 0; %i < %this.getCount(); %i++) + %this.getObject(%i).cleanNonType(%type); +} + +function GameConnection::endMission(%this) +{ + commandToClient(%this, 'MissionEnd', $missionSequence); +} + +//-------------------------------------------------------------------------- +// client start phases: +// 0: start mission +// 1: got phase1 done +// 2: got datablocks done +// 3: got phase2 done +// 4: got phase3 done +function GameConnection::startMission(%this) +{ + // send over the information that will display the server info + // when we learn it got there, we'll send the data blocks + %this.currentPhase = 0; + commandToClient(%this, 'MissionStartPhase1', $missionSequence, $MissionName, MissionGroup.musicTrack); +} + +function serverCmdMissionStartPhase1Done(%client, %seq) +{ + if(%seq != $missionSequence || !$MissionRunning) + return; + + if(%client.currentPhase != 0) + return; + %client.currentPhase = 1; + + // when the datablocks are transmitted, we'll send the ghost always objects + %client.transmitDataBlocks($missionSequence); + +} + +function GameConnection::dataBlocksDone( %client, %missionSequence ) +{ + echo("GOT DATA BLOCKS DONE FOR: " @ %client); + if(%missionSequence != $missionSequence) + return; + + if(%client.currentPhase != 1) + return; + %client.currentPhase = 2; + + // only want to set this once... (targets will not be updated/sent until a + // client has this flag set) + if(!%client.getReceivedDataBlocks()) + { + %client.setReceivedDataBlocks(true); + sendTargetsToClient(%client); + } + + commandToClient(%client, 'MissionStartPhase2', $missionSequence); +} + +function serverCmdMissionStartPhase2Done(%client, %seq) +{ + if(%seq != $missionSequence || !$MissionRunning) + return; + + if(%client.currentPhase != 2) + return; + %client.currentPhase = 3; + + // when all this good love is over, we'll know that the mission lighting is done + %client.transmitPaths(); + + // setup the client team state + if ( $CurrentMissionType !$= "SinglePlayer" ) + serverSetClientTeamState( %client ); + + // start ghosting + %client.activateGhosting(); + %client.camera.scopeToClient(%client); + + // to the next phase... + commandToClient(%client, 'MissionStartPhase3', $missionSequence, $CurrentMission); +} + +function serverCmdMissionStartPhase3Done(%client, %seq) +{ + if(%seq != $missionSequence || !$MissionRunning) + return; + + if(%client.currentPhase != 3) + return; + %client.currentPhase = 4; + + %client.isReady = true; + Game.clientMissionDropReady(%client); +} + +function serverSetClientTeamState( %client ) +{ + // set all player states prior to mission drop ready + + // create a new camera for this client + %client.camera = new Camera() + { + dataBlock = Observer; + }; + + if( isObject( %client.rescheduleVote ) ) + Cancel( %client.rescheduleVote ); + %client.canVote = true; + %client.rescheduleVote = ""; + + MissionCleanup.add( %client.camera ); // we get automatic cleanup this way. + + %observer = false; + if( !$Host::TournamentMode ) // z0dd - ZOD, 9/29/02. Removed T2 demo code from here + { + if( %client.justConnected ) + { + %client.justConnected = false; + %client.camera.getDataBlock().setMode( %client.camera, "justJoined" ); + } + else + { + // server just changed maps - this guy was here before + if( %client.lastTeam !$= "" ) + { + // see if this guy was an observer from last game + if(%client.lastTeam == 0) + { + %observer = true; + + %client.camera.getDataBlock().setMode( %client.camera, "ObserverFly" ); + } + else // let this player join the team he was on last game + { + if(Game.numTeams > 1 && %client.lastTeam <= Game.numTeams ) + { + Game.clientJoinTeam( %client, %client.lastTeam, false ); + } + else + { + Game.assignClientTeam( %client ); + + // spawn the player + Game.spawnPlayer( %client, false ); + } + } + } + else + { + Game.assignClientTeam( %client ); + + // spawn the player + Game.spawnPlayer( %client, false ); + } + + if( !%observer ) + { + if(!$MatchStarted && !$CountdownStarted) + %client.camera.getDataBlock().setMode( %client.camera, "pre-game", %client.player ); + else if(!$MatchStarted && $CountdownStarted) + %client.camera.getDataBlock().setMode( %client.camera, "pre-game", %client.player ); + } + } + } + else + { + // don't need to do anything. MissionDrop will handle things from here. + } +} + +//function serverCmdPreviewDropReady( %client ) +//{ +// $MatchStarted = true; +// commandToClient( %client, 'SetMoveKeys', true); +// %markerObj = "0 0 0"; +// %client.camera.mode = "PreviewMode"; +// %client.camera.setTransform( %markerObj ); +// %client.camera.setFlyMode(); +// +// %client.setControlObject( %client.camera ); +//} + +function HideHudHACK(%visible) +{ + //compassHud.setVisible(%visible); + //enerDamgHud.setVisible(%visible); + retCenterHud.setVisible(%visible); + reticleFrameHud.setVisible(%visible); + //invPackHud.setVisible(%visible); + weaponsHud.setVisible(%visible); + outerChatHud.setVisible(%visible); + objectiveHud.setVisible(%visible); + chatHud.setVisible(%visible); + navHud.setVisible(%visible); + //watermarkHud.setVisible(%visible); + hudClusterBack.setVisible(%visible); + inventoryHud.setVisible(%visible); + clockHUD.setVisible(%visible); +} + +function ServerPlay2D(%profile) +{ + for(%idx = 0; %idx < ClientGroup.getCount(); %idx++) + ClientGroup.getObject(%idx).play2D(%profile); +} + +function ServerPlay3D(%profile,%transform) +{ + for(%idx = 0; %idx < ClientGroup.getCount(); %idx++) + ClientGroup.getObject(%idx).play3D(%profile,%transform); +} + +function clientCmdSetFirstPerson(%value) +{ + $firstPerson = %value; + if(%value) + ammoHud.setVisible(true); + else + ammoHud.setVisible(false); +} + +function clientCmdGetFirstPerson() +{ + commandToServer('FirstPersonValue', $firstPerson); +} + +function serverCmdFirstPersonValue(%client, %firstPerson) +{ + %client.player.firstPerson = %firstPerson; +} + +function clientCmdVehicleMount() +{ + if ( $pref::toggleVehicleView ) + { + $wasFirstPerson = $firstPerson; + $firstPerson = false; + } +} + +function clientCmdVehicleDismount() +{ + if ( $pref::toggleVehicleView ) + $firstPerson = $wasFirstPerson; +} + +//---------------------------------------------------- +// z0dd - ZOD, 3/09/02. Re-write. It's more flexible. +function serverCmdSAD(%client, %password) +{ + if(%password $= "") + { + messageClient(%client, 'MsgPasswordFailed', '\c2You did not supply a PW.'); + return; + } + %name = %client.name; + + switch$ (%password) + { + case $Host::ClassicSuperAdminPassword: + if(!%client.isSuperAdmin) + { + if(%password $= "changeme") + { + messageClient(%client, 'MsgPasswordFailed', '\c2Illegal SAD PW. You need to change the default \"$Host::ClassicSuperAdminPassword\" value in \"ServerPrefs.cs\"!'); + return; + } + %client.isAdmin = true; + %client.isSuperAdmin = true; + MessageAll( 'MsgSuperAdminPlayer', '\c2%2 has become a Super Admin by force.', %client, %name); + logEcho(%client.nameBase @ " has become a Super Admin by force."); + } + + case $Host::AdminPassword: + if(!%client.isAdmin) + { + if(%password $= "changethis") + { + messageClient(%client, 'MsgPasswordFailed', '\c2Illegal Admin PW. You need to change the default \"$Host::AdminPassword\" value in \"ServerPrefs.cs\"!'); + return; + } + %client.isAdmin = true; + %client.isSuperAdmin = false; + MessageAll( 'MsgAdminForce', '\c2%2 has become a Admin by force.', %client, %name); + logEcho(%client.nameBase @ " has become an Admin by force."); + } + default: + messageClient(%client, 'MsgPasswordFailed', '\c2Illegal SAD PW.'); + %client.SadAttempts++; + if(%client.SadAttempts >= 6 && !%client.isSuperAdmin) + { + %client.getAddress(); + %client.getAuthInfo(); + messageClient(%client, 'onClientBanned', 'For attempting to exploit SAD to gain unauthorized Admin by entering\ntoo many passwords, you are being Banned'); + if( isObject(%client.player) ) + { + %client.player.scriptKill(0); + %client.schedule(700, "delete"); + } + schedule(10, %client @ "ResetSadAttp", %client); + %client.setDisconnectReason( 'For attempting to exploit SAD to gain unauthorized Admin by entering\ntoo many passwords, you are being Banned.' ); + %client.schedule(700, "delete"); + BanList::add(%client.guid, %client.getAddress(), $Host::BanTime); + logEcho(%client.nameBase @ " " @ %client.guid @ " has been banned for excessive use of SAD"); + } + } +} + +function ResetSadAttp(%client) +{ + %client.SadAttempts = 0; +} + +//--------------------------------------------------------------- +// z0dd - ZOD, 8/13/02. Added this function. Writen by Writer +// +// Returns true if %text consists of nothing but digits and/or +// decimals. +// Note: rejects strings with more than one decimal, or with a + +// or - as anything but the first character (+ or - are only +// allowed as the first character in the string) +function isNumber(%text) +{ + %dot_count = 0; + for(%i = 0; (%char = getSubStr(%text, %i, 1)) !$= ""; %i++) + { + switch$(%char) + { + case "0": + continue; + case "1": + continue; + case "2": + continue; + case "3": + continue; + case "4": + continue; + case "5": + continue; + case "6": + continue; + case "7": + continue; + case "8": + continue; + case "9": + continue; + case ".": + if(%dot_count > 1) + return false; + + %dot_count++; + continue; + case "-": + if(%i) // only valid as first character + return false; + + continue; + case "+": + if(%i) // only valid as first character + return false; + + continue; + default: + return false; + } + } + // %text passed the test + return true; +} +//--------------------------------------------------------------- + +//---------------------------------------------------- +// z0dd - ZOD, 5/21/03. Replaced by function below. Kept for backward compat +function serverCmdSADSetPassword(%client, %password) +{ + if(%client.isSuperAdmin) + { + if(%password !$= "") + { + if(%password $= "remove") + { + $Host::Password = ""; + $BackupPassword = ""; + } + else + { + $Host::Password = %password; + $BackupPassword = %password; + + // turn autoPW off - ZOD 8/10/03. Why are we doing this? + //$Host::ClassicAutoPWEnabled = 0; + } + export( "$Host::*", $serverprefs, false ); + messageAll( 'MsgServerPassword', '\c3%1\c2: JOIN PASSWORD CHANGED.~wfx/misc/diagnostic_on.wav', %client.name); + if(%password $= "remove") + messageClient(%client, 'MsgServerPassword', '\c2Join PW removed.'); + else + messageClient(%client, 'MsgServerPassword', '\c2Join PW changed to: \c3%1\c2.', addTaggedString(%password)); + + logEcho(%client.nameBase @ " changed the join password.", 1); + } + else + messageClient(%client, 'MsgValueFailed', '\c2No Changes. You did not supply a value. Use \"remove\" to remove join pw.'); + } + else + messageClient(%client, 'MsgNotSuperAdmin', '\c2Only Super Admins can use that command.'); +} +//---------------------------------------------------- + +//--------------------------------------------------------- +// z0dd - ZOD, 3/10/02. New remote admin control function +function serverCmdSet(%client, %type, %val) +{ + // USAGE: commandToServer('Set', type, value); + %type = deTag(%type); + %val = deTag(%val); + + if(!%client.isSuperAdmin) + { + messageClient(%client, 'MsgNotSuperAdmin', '\c2Only Super Admins can use that command.'); + return; + } + if(%type $= "") + { + messageClient(%client, 'MsgTypeFailed', '\c2No Changes. You did not supply a type.'); + return; + } + //if( (%val $= "") && (%type !$= "joinpw") ) + if(%val $= "") + { + if(%type $= "joinpw") + messageClient(%client, 'MsgValueFailed', '\c2No Changes. You did not supply a value. Use \"remove\" to remove join pw.'); + else + messageClient(%client, 'MsgValueFailed', '\c2No Changes. You did not supply a value.'); + return; + } + %name = %client.name; + switch$ (%type) + { + case "superpw": + $Host::ClassicSuperAdminPassword = %val; + export( "$Host::*", $serverprefs, false ); + messageClient(%client, 'MsgSuperPassword', '\c2\"Super Admin\" PW changed to: \c3%1\c2.', addTaggedString(%val)); + logEcho(%client.nameBase @ " changed the Super Admin password.", 1); + + case "adminpw": + $Host::AdminPassword = %val; + export( "$Host::*", $serverprefs, false ); + messageClient(%client, 'MsgAdminPassword', '\c2\"Admin\" PW changed to: \c3%1\c2.', addTaggedString(%val)); + logEcho(%client.nameBase @ " changed the Admin password.", 1); + + case "joinpw": + if(%val $= "remove") + { + $Host::Password = ""; + $BackupPassword = ""; + } + else + { + $Host::Password = %val; + $BackupPassword = %val; + + // turn autoPW off - ZOD 8/10/03. Why are we doing this? + //$Host::ClassicAutoPWEnabled = 0; + } + + export( "$Host::*", $serverprefs, false ); + messageAll( 'MsgServerPassword', '\c3%1\c2: JOIN PASSWORD CHANGED.~wfx/misc/diagnostic_on.wav', %name); + if(%val $= "remove") + messageClient(%client, 'MsgServerPassword', '\c2Join PW removed.'); + else + messageClient(%client, 'MsgServerPassword', '\c2Join PW changed to: \c3%1\c2.', addTaggedString(%val)); + + logEcho(%client.nameBase @ " changed the join password.", 1); + + case "maxplayers": + if(isNumber(%val) && (%val > 0)) + { + $Host::MaxPlayers = %val; + export( "$Host::*", $serverprefs, false ); + messageAll( 'MsgMaxPlayersSet', '\c3%1\c2: PLAYER LIMIT CHANGED TO: \c3%2\c2.~wfx/misc/diagnostic_on.wav', %name, %val); + logEcho(%client.nameBase @ " changed the Player Limit.", 1); + } + else + { + messageClient( %client, 'MsgAdmin', '\c2Value must be a positive number.' ); + } + + case "restart": + if (%val $= "0") + { + $AutoRestart = 0; + messageClient( %client, 'MsgAdmin', '\c2Server restart at mission end aborted.' ); + messageAll( 'MsgServerRestart', '\c3%1\c2: SERVER RESTART HAS BEEN CANCELED.~wfx/misc/diagnostic_on.wav', %name); + } + else if (%val $= "1") + { + messageAll( 'MsgServerRestart', '\c3%1\c2: SERVER WILL BE REBOOTING IN 30 SECONDS!.~wfx/misc/red_alert.wav', %name); + schedule(20000, 0, "messageAll", 'MsgServerRestart', '\c2SERVER WILL REBOOT IN 10 SECONDS!.~wfx/misc/hunters_10.wav'); + schedule(30000, 0, quit); + logEcho(%client.nameBase @ " forced a server restrart.", 1); + } + else + { + messageClient( %client, 'MsgAdmin', '\c2Unknown restart value. 0 cancels restart, 1 forces restart.' ); + } + + case "random": + if(%val $= "0" || %val $= "1") + { + if($CurrentMissionType $= TR2) // z0dd - ZOD, 9/17/02. Check for Team Rabbit 2 + { + messageClient( %client, 'MsgAdmin', '\c2This feature is unavailable in Team Rabbit 2.' ); + return; + } + $Host::ClassicRandomizeTeams = $RandomTeams = %val; + export( "$Host::*", $serverprefs, false ); + %detail = ($RandomTeams ? "ENABLED" : "DISABLED"); + messageAll( 'MsgRandomTeams', '\c3%1\c2: RANDOM TEAMS %2. Changes will take place next mission.~wfx/misc/diagnostic_on.wav', %name, %detail); + logEcho(%client.nameBase @ " " @ %detail @ " random teams.", 1); + } + else + { + messageClient( %client, 'MsgAdmin', '\c2Unknown input value. 0 disables Random Teams, 1 enables Random Teams.' ); + } + + case "fairteams": + if(%val $= "0" || %val $= "1") + { + if($CurrentMissionType $= TR2) // z0dd - ZOD, 9/17/02. Check for Team Rabbit 2 + { + messageClient( %client, 'MsgAdmin', '\c2This feature is unavailable in Team Rabbit 2.' ); + return; + } + $Host::ClassicFairTeams = $FairTeams = %val; + export( "$Host::*", $serverprefs, false ); + %detail = ($FairTeams ? "ENABLED" : "DISABLED"); + messageAll( 'MsgFairTeams', '\c3%1\c2: FAIR TEAMS %2.~wfx/misc/diagnostic_on.wav', %name, %detail ); + logEcho(%client.nameBase @ " " @ %detail @ " fair teams.", 1); + } + else + { + messageClient( %client, 'MsgAdmin', '\c2Unknown input value. 0 disables Fair Teams, 1 enables Fair Teams.' ); + } + // z0dd - ZOD, 7/17/03. Allow super admins to issue console commands on the server. + case "consolecmd": + if($Host::ClassicAllowConsoleAccess) + { + eval(%val); + messageClient( %client, 'MsgAdmin', '\c2Command %1 sent to server console.', %val ); + logEcho(%client.nameBase @ "Send the console command " @ %val @ " to the server.", 1); + } + + default: + messageClient(%client, 'MsgValueFailed', '\c2No Changes. You did not specify a valid type.'); + } +} + +//--------------------------------------------------------- +// z0dd - ZOD, 7/12/02. New AutoPW server functions. Sets +// server join password when server reaches x player count. +function AutoPWServer(%val) +{ + if(%val) + { + if($Host::ClassicAutoPWPassword !$= "changeit") + { + $Host::Password = $Host::ClassicAutoPWPassword; + } + } + else + { + $Host::Password = $BackupPassword; + } + +// z0dd - ZOD, 9/27/02, Chat was being spammed every time someone joined if limit was hit or above +// %detail = (($Host::Password $= "") ? "removed" : "set"); +// messageAll( 'MsgAdmin', '\c2Join password %1 by Auto-password feature.', %detail ); +} + +function serverCmdAutoPWSetup(%client, %type, %val) +{ + // USAGE: commandToServer('AutoPWSetup', type, value); + %type = deTag(%type); + %val = deTag(%val); + if(!%client.isSuperAdmin) + { + messageClient(%client, 'MsgNotSuperAdmin', '\c2Only Super Admins can use this command.'); + return; + } + if(%type $= "") + { + messageClient(%client, 'MsgTypeFailed', '\c2No Changes. You did not supply a type.'); + return; + } + switch$ (%type) + { + case "autopw": + if (%val $= "0") + { + $Host::ClassicAutoPWEnabled = 0; + AutoPWServer(0); + messageClient( %client, 'MsgAdmin', '\c2Auto-password disabled.' ); + } + else if (%val $= "1") + { + $Host::ClassicAutoPWEnabled = 1; + messageClient( %client, 'MsgAdmin', '\c2Auto-password enabled.' ); + logEcho(%client.nameBase @ " enabled Auto-password.", 1); + } + else + { + messageClient( %client, 'MsgAdmin', '\c2Unknown value. 0 disables Auto-password, 1 enables Auto-password.' ); + } + + case "autopwpass": + if(%val !$= "") + { + $Host::ClassicAutoPWPassword = %val; + } + else + { + messageClient( %client, 'MsgAdmin', '\c2You must specify a password.' ); + return; + } + export( "$Host::*", $serverprefs, false ); + messageClient(%client, 'MsgServerPassword', '\c2Server Auto-password PW changed to: \c3%1\c2.', addTaggedString(%val)); + logEcho(%client.nameBase @ " changed the Auto-password PW.", 1); + + case "autopwcount": + if(%val !$= "" && %val !$= "0") + { + $Host::ClassicAutoPWPlayerCount = %val; + } + else + { + messageClient( %client, 'MsgAdmin', '\c2You must specify a numerical value.' ); + return; + } + export( "$Host::*", $serverprefs, false ); + messageClient(%client, 'MsgServerPassword', '\c2Server Auto-password player count changed to: \c3%1\c2.', addTaggedString(%val)); + logEcho(%client.nameBase @ " changed the Auto-password player count.", 1); + } +} + +//--------------------------------------------------------------------------------------------------- +// z0dd - ZOD, 4-15-02. Pick spawn spot by killing self during tourney wait. Also addresses +// team switching to crash server exploit. New function +//$WAIT_PERIOD = 15000; +//$WAIT_MESSAGE = '\c3WAIT MESSAGE:\cr You must wait another %1 seconds'; + +function GameConnection::waitTimeout(%this) +{ + %this.isWaiting = false; +} + +function SpawnPosChange( %client ) +{ + if( isObject( Game ) && %client != Game.kickClient && $Host::TournamentMode && !$CountdownStarted) + { + if (!%client.isWaiting) + { + %client.isWaiting = true; + %client.waitStart = getSimTime(); + %client.schedule(15000, waitTimeout); + + clearBottomPrint(%client); + Game.clientChangeTeam( %client, %client.team, 0, true ); + + if(!$MatchStarted) + { + %client.observerMode = "pregame"; + %client.notReady = true; + %client.camera.getDataBlock().setMode( %client.camera, "pre-game", %client.player ); + %client.setControlObject( %client.camera ); + + if(!$CountdownStarted) + { + %client.notReady = true; + centerprint( %client, "\nPress FIRE when ready.", 0, 3 ); + } + } + else + { + commandToClient(%client, 'setHudMode', 'Standard', "", 0); + } + } + else + { + %wait = mFloor((15000 - (getSimTime() - %client.waitStart)) / 1000); + messageClient(%client, "", '\c3WAIT MESSAGE:\cr You must wait another %1 seconds', %wait); + } + } +} +//--------------------------------------------------------------------------------------------------- + +function serverCmdSuicide(%client) +{ + if(!isObject(%client.player)) // z0dd - ZOD, 4-15-02. Console spam fix. + return; + + // z0dd - ZOD, 4-15-02: Pick spawn spot by killing self during tourney wait + if( $MatchStarted ) + { + %client.player.scriptKill($DamageType::Suicide); + } + else + { + if($CurrentMissionType !$= TR2) // z0dd - ZOD, 9/17/02. Check for Team Rabbit 2 + SpawnPosChange( %client ); + } +} + +function serverCmdToggleCamera(%client) +{ + if ($testcheats || $CurrentMissionType $= "SinglePlayer") + { + %control = %client.getControlObject(); + if (%control == %client.player) + { + %control = %client.camera; + %control.mode = toggleCameraFly; + %control.setFlyMode(); + } + else + { + %control = %client.player; + %control.mode = observerFly; + %control.setFlyMode(); + } + %client.setControlObject(%control); + } +} + +function serverCmdDropPlayerAtCamera(%client) +{ + if ($testcheats) + { + %client.player.setTransform(%client.camera.getTransform()); + %client.player.setVelocity("0 0 0"); + %client.setControlObject(%client.player); + } +} + +function serverCmdDropCameraAtPlayer(%client) +{ + if ($testcheats) + { + %client.camera.setTransform(%client.player.getTransform()); + %client.camera.setVelocity("0 0 0"); + %client.setControlObject(%client.camera); + } +} + +function serverCmdToggleRace(%client) +{ + if ($testcheats) + { + if (%client.race $= "Human") + %client.race = "Bioderm"; + else + %client.race = "Human"; + %client.player.setArmor(%client.armor); + } +} + +function serverCmdToggleGender(%client) +{ + if ($testcheats) + { + if (%client.sex $= "Male") + %client.sex = "Female"; + else + %client.sex = "Male"; + %client.player.setArmor(%client.armor); + } +} + +function serverCmdToggleArmor(%client) +{ + if ($testcheats) + { + if (%client.armor $= "Light") + %client.armor = "Medium"; + else + if (%client.armor $= "Medium") + %client.armor = "Heavy"; + else + %client.armor = "Light"; + %client.player.setArmor(%client.armor); + } +} + +function serverCmdPlayCel(%client,%anim) +{ + if ($testcheats) + { + %anim = %client.player.celIdx; + if (%anim++ > 8) + %anim = 1; + %client.player.setActionThread("cel"@%anim); + %client.player.celIdx = %anim; + } +} + +// NOTENOTENOTE: Review +// ilys - Do not allow animations inside a forcefield. +// ilys - Unmount the mortar and grenade launcher on animation. +function PlayAnim(%client, %anim) +{ + if( %anim $= "Death1" || %anim $= "Death2" || %anim $= "Death3" || %anim $= "Death4" || %anim $= "Death5" || + %anim $= "Death6" || %anim $= "Death7" || %anim $= "Death8" || %anim $= "Death9" || %anim $= "Death10" || + %anim $= "Death11" || %anim $= "sitting" || %anim $= "scoutRoot" || %anim $= "look" || %anim $= "lookms" || + %anim $= "looknw" || %anim $= "head" || %anim $= "headSide" || %anim $= "ski" || %anim $= "light_recoil") + return; + + %player = %client.player; + + // don't play animations if player is in a vehicle + // z0dd - ZOD, 4-15-02. Console spam fix, check for player object. + if(!isObject(%player)) + return; + + // ilys - Check for forcefields + if(%player.isMounted() || %player.isInForceField()) + return; + + %weapon = ( %player.getMountedImage($WeaponSlot) == 0 ) ? "" : %player.getMountedImage($WeaponSlot).getName().item; + if(%weapon $= "MissileLauncher" || %weapon $= "GrenadeLauncher" || %weapon $= "SniperRifle" || %weapon $= "Mortar") + { + %player.animResetWeapon = true; + %player.lastWeapon = %weapon; + %player.unmountImage($WeaponSlot); + // ---------------------------------------------- + // z0dd - ZOD, 5/8/02. %obj is the wrong varible. + //%obj.setArmThread(look); + %player.setArmThread(look); + } + %player.setActionThread(%anim); +} + +function serverCmdPlayDeath(%client,%anim) +{ + if ($testcheats) + { + %anim = %client.player.deathIdx; + if (%anim++ > 11) + %anim = 1; + %client.player.setActionThread("death"@%anim,true); + %client.player.deathIdx = %anim; + } +} + +// NOTENOTENOTE: Review these! +//------------------------------------------------------------ +// TODO - make this function specify a team to switch to... +// z0dd - ZOD - Kaiten 8/10/03. This is retarded, it passes NULL +// as a team which screws up like mad. also it is not used by anything. +// Kill it! +function serverCmdClientTeamChange( %client ) +{ + // pass this to the game object to handle: +// if ( isObject( Game ) && Game.kickClient != %client) +// { +// %fromObs = %client.team == 0; +// if(%fromObs) +// clearBottomPrint(%client); + +// Game.clientChangeTeam( %client, "", %fromObs ); +// } +} + +function serverCanAddBot() +{ + //find out how many bots are already playing + %botCount = 0; + %numClients = ClientGroup.getCount(); + for (%i = 0; %i < %numClients; %i++) + { + %cl = ClientGroup.getObject(%i); + if (%cl.isAIcontrolled()) + %botCount++; + } + + //add only if we have less bots than the bot count, and if there would still be room for a + if ($HostGameBotCount > 0 && %botCount < $Host::botCount && %numClients < $Host::maxPlayers - 1) + return true; + else + return false; +} + +function serverCmdAddBot( %client ) +{ + //only admins can add bots... + if (%client.isAdmin) + { + if (serverCanAddBot()) + aiConnectMultiple( 1, $Host::MinBotDifficulty, $Host::MaxBotDifficulty, -1 ); + } +} + +// --------------------------------------------------------------------------------- +// z0dd - ZOD, 6/22/02. Changed function to use a waiting period when changing teams +// to prevent team change exploit to crash servers. Added admin varible so admins +// can teamchange whoever they want. +function serverCmdClientJoinTeam( %client, %team, %admin ) +{ + // z0dd - ZOD, 4/10/04. ilys - if the client does not enter a team, uses a team less than -1, + // more than the number of teams for the gametype or zero, set his team to -1 (switch) + if(%team $= "" || %team < -1 || %team == 0 || %team > Game.numTeams) + %team = -1; + + if( %team == -1 ) + { + if( %client.team == 1 ) + %team = 2; + else + %team = 1; + } + if ( isObject( Game ) && Game.kickClient != %client) + { + if(%client.team != %team) + { + // z0dd - ZOD, 9/17/02. Fair teams, check for Team Rabbit 2 as well. + if(($FairTeams && !%client.isAdmin) && ($CurrentMissionType !$= TR2)) + { + %otherTeam = %team == 1 ? 2 : 1; + if(!%admin.isAdmin && %team != 0 && ($TeamRank[%team, count]+1) > $TeamRank[%otherTeam, count]) + { + messageClient(%client, 'MsgFairTeams', '\c2Teams will be uneven, please choose another team.'); + return; + } + } + + if(!%client.isWaiting || %admin.isAdmin) + { + %client.isWaiting = true; + %client.waitStart = getSimTime(); + %client.schedule(15000, waitTimeout); + + %fromObs = %client.team == 0; + + if(%fromObs) + clearBottomPrint(%client); + + if( %client.isAIControlled() ) + Game.AIChangeTeam( %client, %team ); + else + Game.clientChangeTeam( %client, %team, %fromObs ); + } + else + { + %wait = mFloor((15000 - (getSimTime() - %client.waitStart)) / 1000); + messageClient(%client, "", '\c3WAIT MESSAGE:\cr You must wait another %1 seconds', %wait); + } + } + } +} +// --------------------------------------------------------------------------------- + +// this should only happen in single team games +function serverCmdClientAddToGame( %client, %targetClient ) +{ + if ( isObject( Game ) ) + Game.clientJoinTeam( %targetClient, 0, $matchstarted ); + + clearBottomPrint(%targetClient); + + if($matchstarted) + { + %targetClient.setControlObject( %targetClient.player ); + commandToClient(%targetClient, 'setHudMode', 'Standard'); + } + else + { + %targetClient.notReady = true; + %targetClient.camera.getDataBlock().setMode( %targetClient.camera, "pre-game", %targetClient.player ); + %targetClient.setControlObject( %targetClient.camera ); + } + + if( $Host::TournamentMode && !$CountdownStarted) // z0dd - ZOD, 9/29/02. Removed T2 demo code from here + { + %targetClient.notReady = true; + centerprint( %targetClient, "\nPress FIRE when ready.", 0, 3 ); + } +} + +function serverCmdClientJoinGame( %client ) +{ + // z0dd - ZOD, 4/10/04. ilys - Only allow the client to force play when the match has started + if(!$MatchStarted) + return; + + if ( isObject( Game ) ) + Game.clientJoinTeam( %client, 0, 1 ); + + %client.setControlObject( %client.player ); + clearBottomPrint(%client); + commandToClient(%client, 'setHudMode', 'Standard'); +} + +function serverCmdClientMakeObserver( %client ) +{ + if ( isObject( Game ) && Game.kickClient != %client ) + Game.forceObserver( %client, "playerChoose" ); +} + +function serverCmdChangePlayersTeam( %clientRequesting, %client, %team) +{ + if( isObject( Game ) && %client != Game.kickClient && %clientRequesting.isAdmin) + { + // z0dd - ZOD, 6/22/02. Added admin varible to enable admins to teamchange + // even players under the Wait timer. + //serverCmdClientJoinTeam(%client, %team); + serverCmdClientJoinTeam(%client, %team, %clientRequesting); + + if(!$MatchStarted) + { + %client.observerMode = "pregame"; + %client.notReady = true; + %client.camera.getDataBlock().setMode( %client.camera, "pre-game", %client.player ); + %client.setControlObject( %client.camera ); + + if( $Host::TournamentMode && !$CountdownStarted) // z0dd - ZOD, 9/29/02. Removed T2 demo code from here + { + %client.notReady = true; + centerprint( %client, "\nPress FIRE when ready.", 0, 3 ); + } + } + else + commandToClient(%client, 'setHudMode', 'Standard', "", 0); + + %multiTeam = (Game.numTeams > 1); + + %aname = %clientRequesting.name; // z0dd - ZOD, 4-15-02. who did what + %name = %client.name; // z0dd - ZOD, 4-15-02. who did what + + if(%multiTeam) + { + messageClient( %client, 'MsgClient', '\c2%1 has changed your team.', %aname); // z0dd - ZOD, 4-15-02. who did what + messageAllExcept( %client, -1, 'MsgClient', '\c2%1 forced %2 to join the %3 team.', %aname, %name, game.getTeamName(%client.team) ); // z0dd - ZOD, 4-15-02. who did what + } + else + { + messageClient( %client, 'MsgClient', '\c2%1 has added you to the game.', %aname); // z0dd - ZOD, 4-15-02. who did what + messageAllExcept( %client, -1, 'MsgClient', '\c2%1 added %2 to the game.', %aname, %name); // z0dd - ZOD, 4-15-02. who did what + } + } +} + +//--------------------------------------------------------------------------------------------------------- +// z0dd - ZOD 7/12/03. Allow SuperAdmins to De-Admin normal Admins +function serverCmdStripAdmin(%client, %admin) +{ + if(!%admin.isAdmin || !%client.isAdmin) + return; + + if(%client $= %admin) + { + %admin.isAdmin = 0; + %admin.isSuperAdmin = 0; + messageClient(%admin, 'MsgStripAdminPlayer', 'You have stripped yourself of admin privledges.'); + logEcho(%client.nameBase @ " stripped admin from " @ %admin.nameBase, 1); + return; + } + else if(%client.isSuperAdmin) + { + messageAll( 'MsgStripAdminPlayer', '\c2%1 removed %2\'s admin privledges.', %client.name, %admin.name, %admin ); + messageClient(%admin, 'MsgStripAdminPlayer', 'You are being stripped of your admin privledges by %1.', %client.name); + %admin.isAdmin = 0; + %admin.isSuperAdmin = 0; + logEcho(%client.nameBase @ " stripped admin from " @ %admin.nameBase, 1); + } + else + messageClient(%client, 'MsgError', '\c2Only Super Admins can use this command.'); +} + +// z0dd - ZOD 4/18/02. Allow Admins to warn players +function serverCmdWarnPlayer(%client, %target) +{ + if(%client.isAdmin) + { + messageAllExcept(%target, -1, 'MsgAdminForce', '%1 has been warned for inappropriate conduct by %2.', %target.name, %client.name); + messageClient(%target, 'MsgAdminForce', 'You are recieving this warning for inappropriate conduct by %1. Behave or you will be kicked..~wfx/misc/lightning_impact.wav', %client.name); + centerprint(%target, "You are recieving this warning for inappropriate conduct.\nBehave or you will be kicked.", 10, 2); + logEcho(%client.nameBase @ " sent warning to " @ %target.nameBase); + } + else + messageClient(%client, 'MsgError', '\c2Only Admins can use this command.'); +} + +//--------------------------------------------------------------------------------------------------------- + +function serverCmdForcePlayerToObserver( %clientRequesting, %client ) +{ + if( isObject( Game ) && %clientRequesting.isAdmin) + Game.forceObserver( %client, "adminForce" ); +} + +//-------------------------------------------------------------------------- + +function serverCmdTogglePlayerMute(%client, %who) +{ + if (%client.muted[%who]) + { + %client.muted[%who] = false; + messageClient(%client, 'MsgPlayerMuted', '%1 has been unmuted.', %who.name, %who, false); + } + else + { + %client.muted[%who] = true; + messageClient(%client, 'MsgPlayerMuted', '%1 has been muted.', %who.name, %who, true); + } +} + +//-------------------------------------------------------------------------- +// VOTE MENU FUNCTIONS: +function serverCmdGetVoteMenu( %client, %key ) +{ + if ( isObject( Game ) ) + Game.sendGameVoteMenu( %client, %key ); +} + +function serverCmdGetPlayerPopupMenu( %client, %targetClient, %key ) +{ + if ( isObject( Game ) ) + Game.sendGamePlayerPopupMenu( %client, %targetClient, %key ); +} + +function serverCmdGetTeamList( %client, %key ) +{ + if ( isObject( Game ) ) + Game.sendGameTeamList( %client, %key ); +} + +function serverCmdGetMissionTypes( %client, %key ) +{ + for ( %type = 0; %type < $HostTypeCount; %type++ ) + messageClient( %client, 'MsgVoteItem', "", %key, %type, "", $HostTypeDisplayName[%type], true ); +} + +function serverCmdGetMissionList( %client, %key, %type ) +{ + if ( %type < 0 || %type >= $HostTypeCount ) + return; + + for ( %i = $HostMissionCount[%type] - 1; %i >= 0; %i-- ) + { + %idx = $HostMission[%type, %i]; + + // If we have bots, don't change to a mission that doesn't support bots: + if ( $HostGameBotCount > 0 ) + { + if( !$BotEnabled[%idx] ) + continue; + } + + messageClient( %client, 'MsgVoteItem', "", %key, + %idx, // mission index, will be stored in $clVoteCmd + "", + $HostMissionName[%idx], + true ); + } +} + +function serverCmdGetTimeLimitList( %client, %key, %type ) +{ + if ( isObject( Game ) ) + Game.sendTimeLimitList( %client, %key ); +} + +function serverCmdClientPickedTeam( %client, %option ) +{ + // z0dd - ZOD, 4/10/04. ilys - Do not allow the Tourney Pick Team Dialog to function when no in Tourney mode + if(!$Host::TournamentMode) + return; + + // ------------------------------------------------------------------------------------- + // z0dd - ZOD 4/18/02. Tourney mode bug fix. Fix provided by FSB-AO + // Bug description: In tournament mode, If a player is teamchanged by an admin before + // they select a team, the server just changes their team and re-skins the player. They + // are not moved from their initial spawn point, meaning they could spawn very close to + // the other teams flag. This script kills the player if they are already teamed when + // they select an option and spawns them on the correct side of the map. + switch(%option) + { + case 1: + if ( isObject(%client.player) ) + { + %client.player.scriptKill(0); + Game.clientChangeTeam(%client, %option, 0); + } + else + Game.clientJoinTeam( %client, %option, false ); + case 2: + if ( isObject(%client.player) ) + { + %client.player.scriptKill(0); + Game.clientChangeTeam(%client, %option, 0); + } + else + Game.clientJoinTeam( %client, %option, false ); + case 3: + if( !isObject(%client.player) ) + { + Game.assignClientTeam( %client, $MatchStarted ); + Game.spawnPlayer( %client, false ); + } + default: + if( isObject(%client.player) ) + { + %client.player.scriptKill(0); + ClearBottomPrint(%client); + } + Game.forceObserver( %client, "playerChoose" ); + %client.observerMode = "observer"; + %client.notReady = false; + return; + } + // ------------------------------------------------------------------------------------- + + ClearBottomPrint(%client); + %client.observerMode = "pregame"; + %client.notReady = true; + %client.camera.getDataBlock().setMode( %client.camera, "pre-game", %client.player ); + commandToClient(%client, 'setHudMode', 'Observer'); + %client.setControlObject( %client.camera ); + centerprint( %client, "\nPress FIRE when ready.", 0, 3 ); +} + +function playerPickTeam( %client ) +{ + %numTeams = Game.numTeams; + + if(%numTeams > 1) + { + %client.camera.mode = "PickingTeam"; + schedule( 0, 0, "commandToClient", %client, 'pickTeamMenu', Game.getTeamName(1), Game.getTeamName(2)); + } + else + { + Game.clientJoinTeam(%client, 0, 0); + %client.observerMode = "pregame"; + %client.notReady = true; + %client.camera.getDataBlock().setMode( %client.camera, "pre-game", %client.player ); + centerprint( %client, "\nPress FIRE when ready.", 0, 3 ); + %client.setControlObject( %client.camera ); + } +} + +function serverCmdPlayContentSet( %client ) +{ + if( $Host::TournamentMode && !$CountdownStarted && !$MatchStarted ) // z0dd - ZOD, 9/29/02. Removed T2 demo code from here + playerPickTeam( %client ); +} + +//-------------------------------------------------------------------------- +// This will probably move elsewhere... +function getServerStatusString() +{ + return isObject(Game) ? Game.getServerStatusString() : "NoGame"; +} + + +function dumpGameString() +{ + error( getServerStatusString() ); +} + +function isOnAdminList(%client) +{ + if( !%totalRecords = getFieldCount( $Host::AdminList ) ) + { + return false; + } + + for(%i = 0; %i < %totalRecords; %i++) + { + %record = getField( getRecord( $Host::AdminList, 0 ), %i); + if(%record == %client.guid) + return true; + } + + return false; +} + +function isOnSuperAdminList(%client) +{ + if( !%totalRecords = getFieldCount( $Host::superAdminList ) ) + { + return false; + } + + for(%i = 0; %i < %totalRecords; %i++) + { + %record = getField( getRecord( $Host::superAdminList, 0 ), %i); + if(%record == %client.guid) + return true; + } + + return false; +} + +function ServerCmdAddToAdminList( %admin, %client ) +{ + if( !%admin.isSuperAdmin ) + return; + + %count = getFieldCount( $Host::AdminList ); + + for ( %i = 0; %i < %count; %i++ ) + { + %id = getField( $Host::AdminList, %i ); + if ( %id == %client.guid ) + { + return; // They're already there! + } + } + + if( %count == 0 ) + $Host::AdminList = %client.guid; + else + $Host::AdminList = $Host::AdminList TAB %client.guid; + + // z0dd - ZOD, 4/29/02. Was not exporting to serverPrefs and did not message admin + export( "$Host::*", $serverprefs, false ); + messageClient(%admin, 'MsgAdmin', '\c3\"%1\"\c2 added to Admin list: \c3%2\c2.', %client.name, %client.guid); + logEcho(%admin.nameBase @ " added " @ %client.nameBase @ " " @ %client.guid @ " to Admin list.", 1); +} + +function ServerCmdAddToSuperAdminList( %admin, %client ) +{ + if( !%admin.isSuperAdmin ) + return; + + %count = getFieldCount( $Host::SuperAdminList ); + + for ( %i = 0; %i < %count; %i++ ) + { + %id = getField( $Host::SuperAdminList, %i ); + if ( %id == %client.guid ) + return; // They're already there! + } + + if( %count == 0 ) + $Host::SuperAdminList = %client.guid; + else + $Host::SuperAdminList = $Host::SuperAdminList TAB %client.guid; + + // z0dd - ZOD, 4/29/02. Was not exporting to serverPrefs and did not message admin + export( "$Host::*", $serverprefs, false ); + messageClient(%admin, 'MsgAdmin', '\c3\"%1\"\c2 added to Super Admin list: \c3%2\c2.', %client.name, %client.guid); + logEcho(%admin.nameBase @ " added " @ %client.nameBase @ " " @ %client.guid @ " to Super Admin list.", 1); +} + +function resetTournamentPlayers() +{ + %count = ClientGroup.getCount(); + for( %i = 0; %i < %count; %i++ ) + { + %cl = ClientGroup.getObject(%i); + %cl.notready = 1; + %cl.notReadyCount = ""; + } +} + +function forceTourneyMatchStart() +{ + %playerCount = 0; + %count = ClientGroup.getCount(); + for( %i = 0; %i < %count; %i++ ) + { + %cl = ClientGroup.getObject(%i); + if(%cl.camera.Mode $= "pre-game") + %playerCount++; + } + + // don't start the mission until we have players + if(%playerCount == 0) + { + return false; + } + + %count = ClientGroup.getCount(); + for( %i = 0; %i < %count; %i++ ) + { + %cl = ClientGroup.getObject(%i); + if(%cl.camera.Mode $= "pickingTeam") + { + // throw these guys into observer mode + if(Game.numTeams > 1) + commandToClient( %cl, 'processPickTeam'); // clear the pickteam menu + Game.forceObserver( %cl, "adminForce" ); + } + } + return true; +} + +function startTourneyCountdown() +{ + %count = ClientGroup.getCount(); + for( %i = 0; %i < %count; %i++ ) + { + %cl = ClientGroup.getObject(%i); + ClearCenterPrint(%cl); + ClearBottomPrint(%cl); + } + + // lets get it on! + Countdown( 30 * 1000 ); +} + +function checkTourneyMatchStart() +{ + if( $CountdownStarted || $matchStarted ) + return; + + // loop through all the clients and see if any are still notready + %playerCount = 0; + %notReadyCount = 0; + + %count = ClientGroup.getCount(); + for( %i = 0; %i < %count; %i++ ) + { + %cl = ClientGroup.getObject(%i); + if(%cl.camera.mode $= "pickingTeam") + { + %notReady[%notReadyCount] = %cl; + %notReadyCount++; + } + else if(%cl.camera.Mode $= "pre-game") + { + if(%cl.notready) + { + %notReady[%notReadyCount] = %cl; + %notReadyCount++; + } + else + { + %playerCount++; + } + } + else if(%cl.camera.Mode $= "observer") + { + // this guy is watching + } + } + + if(%notReadyCount) + { + if(%notReadyCount == 1) + MessageAll( 'msgHoldingUp', '\c1%1 is holding things up!', %notReady[0].name); + else if(%notReadyCount < 4) + { + for(%i = 0; %i < %notReadyCount - 2; %i++) + %str = getTaggedString(%notReady[%i].name) @ ", " @ %str; + + %str = "\c2" @ %str @ getTaggedString(%notReady[%i].name) @ " and " @ getTaggedString(%notReady[%i+1].name) + @ " are holding things up!"; + MessageAll( 'msgHoldingUp', %str ); + } + return; + } + + if(%playerCount != 0) + { + %count = ClientGroup.getCount(); + for( %i = 0; %i < %count; %i++ ) + { + %cl = ClientGroup.getObject(%i); + %cl.notready = ""; + %cl.notReadyCount = ""; + ClearCenterPrint(%cl); + ClearBottomPrint(%cl); + } + + if ( Game.scheduleVote !$= "" && Game.voteType $= "VoteMatchStart") + { + messageAll('closeVoteHud', ""); + cancel(Game.scheduleVote); + Game.scheduleVote = ""; + } + + Countdown(30 * 1000); + } +} + +function checkMissionStart() +{ + %readyToStart = false; + for(%clientIndex = 0; %clientIndex < ClientGroup.getCount(); %clientIndex++) + { + %client = ClientGroup.getObject(%clientIndex); + if(%client.isReady) + { + %readyToStart = true; + break; + } + } + + if(%readyToStart || ClientGroup.getCount() < 1) + { + if($Host::warmupTime > 0 && $CurrentMissionType !$= "SinglePlayer") + countDown($Host::warmupTime * 1000); + else + Game.startMatch(); + + // z0dd - ZOD, 5/12/04. First off, there is no time specified. Secondly, this isn't + // in checkTourneyMatchStart, which it should have been. We do way with this and + // handle it in vehicle::onAdd. + //for(%x = 0; %x < $NumVehiclesDeploy; %x++) + // $VehiclesDeploy[%x].getDataBlock().schedule(%timeMS / 2, "vehicleDeploy", $VehiclesDeploy[%x], 0, 1); + //$NumVehiclesDeploy = 0; + } + else + { + schedule(2000, ServerGroup, "checkMissionStart"); + } +} + +function Countdown(%timeMS) +{ + if($countdownStarted) + return; + + echo("starting mission countdown..."); + + if(isObject(Game)) + %game = Game.getId(); + else + return; + + $countdownStarted = true; + Game.matchStart = Game.schedule( %timeMS, "StartMatch" ); + + if (%timeMS > 30000) + notifyMatchStart(%timeMS); + + if(%timeMS >= 30000) + Game.thirtyCount = schedule(%timeMS - 30000, Game, "notifyMatchStart", 30000); + if(%timeMS >= 15000) + Game.fifteenCount = schedule(%timeMS - 15000, Game, "notifyMatchStart", 15000); + if(%timeMS >= 10000) + Game.tenCount = schedule(%timeMS - 10000, Game, "notifyMatchStart", 10000); + if(%timeMS >= 5000) + Game.fiveCount = schedule(%timeMS - 5000, Game, "notifyMatchStart", 5000); + if(%timeMS >= 4000) + Game.fourCount = schedule(%timeMS - 4000, Game, "notifyMatchStart", 4000); + if(%timeMS >= 3000) + Game.threeCount = schedule(%timeMS - 3000, Game, "notifyMatchStart", 3000); + if(%timeMS >= 2000) + Game.twoCount = schedule(%timeMS - 2000, Game, "notifyMatchStart", 2000); + if(%timeMS >= 1000) + Game.oneCount = schedule(%timeMS - 1000, Game, "notifyMatchStart", 1000); +} + +function EndCountdown(%timeMS) +{ + echo("mission end countdown..."); + + if(isObject(Game)) + %game = Game.getId(); + else + return; + + if(%timeMS >= 180000) + Game.endthreeminuteCount = schedule(%timeMS - 180000, Game, "notifyMatchEndMinutes", 180000); + if(%timeMS >= 120000) + Game.endtwominuteCount = schedule(%timeMS - 120000, Game, "notifyMatchEndMinutes", 120000); + if(%timeMS >= 60000) + Game.endsixtyCount = schedule(%timeMS - 60000, Game, "notifyMatchEnd", 60000); + if(%timeMS >= 30000) + Game.endthirtyCount = schedule(%timeMS - 30000, Game, "notifyMatchEnd", 30000); + if(%timeMS >= 10000) + Game.endtenCount = schedule(%timeMS - 10000, Game, "notifyMatchEnd", 10000); + if(%timeMS >= 5000) + Game.endfiveCount = schedule(%timeMS - 5000, Game, "notifyMatchEnd", 5000); + if(%timeMS >= 4000) + Game.endfourCount = schedule(%timeMS - 4000, Game, "notifyMatchEnd", 4000); + if(%timeMS >= 3000) + Game.endthreeCount = schedule(%timeMS - 3000, Game, "notifyMatchEnd", 3000); + if(%timeMS >= 2000) + Game.endtwoCount = schedule(%timeMS - 2000, Game, "notifyMatchEnd", 2000); + if(%timeMS >= 1000) + Game.endoneCount = schedule(%timeMS - 1000, Game, "notifyMatchEnd", 1000); +} + +function CancelCountdown() +{ + if(Game.sixtyCount !$= "") + cancel(Game.sixtyCount); + if(Game.thirtyCount !$= "") + cancel(Game.thirtyCount); + if(Game.fifteenCount !$= "") + cancel(Game.fifteenCount); + if(Game.tenCount !$= "") + cancel(Game.tenCount); + if(Game.fiveCount !$= "") + cancel(Game.fiveCount); + if(Game.fourCount !$= "") + cancel(Game.fourCount); + if(Game.threeCount !$= "") + cancel(Game.threeCount); + if(Game.twoCount !$= "") + cancel(Game.twoCount); + if(Game.oneCount !$= "") + cancel(Game.oneCount); + if(isObject(Game)) + cancel(Game.matchStart); + + Game.matchStart = ""; + Game.thirtyCount = ""; + Game.fifteenCount = ""; + Game.tenCount = ""; + Game.fiveCount = ""; + Game.fourCount = ""; + Game.threeCount = ""; + Game.twoCount = ""; + Game.oneCount = ""; + + $countdownStarted = false; +} + +function CancelEndCountdown() +{ + //cancel the mission end countdown... + if(Game.endthreeminuteCount !$= "") + cancel(Game.endthreeminuteCount); + if(Game.endtwominuteCount !$= "") + cancel(Game.endtwominuteCount); + if(Game.endsixtyCount !$= "") + cancel(Game.endsixtyCount); + if(Game.endthirtyCount !$= "") + cancel(Game.endthirtyCount); + if(Game.endtenCount !$= "") + cancel(Game.endtenCount); + if(Game.endfiveCount !$= "") + cancel(Game.endfiveCount); + if(Game.endfourCount !$= "") + cancel(Game.endfourCount); + if(Game.endthreeCount !$= "") + cancel(Game.endthreeCount); + if(Game.endtwoCount !$= "") + cancel(Game.endtwoCount); + if(Game.endoneCount !$= "") + cancel(Game.endoneCount); + + Game.endmatchStart = ""; + Game.endthirtyCount = ""; + Game.endtenCount = ""; + Game.endfiveCount = ""; + Game.endfourCount = ""; + Game.endthreeCount = ""; + Game.endtwoCount = ""; + Game.endoneCount = ""; +} + +function resetServerDefaults() +{ + $resettingServer = true; + echo( "Resetting server defaults..." ); + + if( isObject( Game ) ) + Game.gameOver(); + + // Override server defaults with prefs: + exec( "scripts/ServerDefaults.cs" ); + exec( $serverprefs ); + + // --------------------------------------------------- + // z0dd - ZOD, 9/29/02. Removed T2 demo code from here + //convert the team skin and name vars to tags... + %index = 0; + while ($Host::TeamSkin[%index] !$= "") + { + $TeamSkin[%index] = addTaggedString($Host::TeamSkin[%index]); + %index++; + } + + %index = 0; + while ($Host::TeamName[%index] !$= "") + { + $TeamName[%index] = addTaggedString($Host::TeamName[%index]); + %index++; + } + + // Get the hologram names from the prefs... + %index = 1; + while ( $Host::holoName[%index] !$= "" ) + { + $holoName[%index] = $Host::holoName[%index]; + %index++; + } + // --------------------------------------------------- + + // kick all bots... + removeAllBots(); + + // add bots back if they were there before.. + if( $Host::botsEnabled ) // z0dd - ZOD, 9/29/02. Removed T2 demo code from here + initGameBots( $Host::Map, $Host::MissionType ); + + // load the missions + loadMission( $Host::Map, $Host::MissionType ); + $resettingServer = false; + echo( "Server reset complete." ); +} + +function removeAllBots() +{ + while( ClientGroup.getCount() ) + { + %client = ClientGroup.getObject(0); + if(%client.isAIControlled()) + %client.drop(); + else + %client.delete(); + } +} + +//------------------------------------------------------------------------------ +function getServerGUIDList() +{ + %count = ClientGroup.getCount(); + for ( %i = 0; %i < %count; %i++ ) + { + %cl = ClientGroup.getObject( %i ); + if ( isObject( %cl ) && !%cl.isSmurf && !%cl.isAIControlled() ) + { + %guid = getField( %cl.getAuthInfo(), 3 ); + if ( %guid != 0 ) + { + if ( %list $= "" ) + %list = %guid; + else + %list = %list TAB %guid; + } + } + } + + return( %list ); +} + +//------------------------------------------------------------------------------ +// will return the first admin found on the server +function getAdmin() +{ + %admin = 0; + for ( %clientIndex = 0; %clientIndex < ClientGroup.getCount(); %clientIndex++ ) + { + %cl = ClientGroup.getObject( %clientIndex ); + if(%cl.isAdmin || %cl.isSuperAdmin) + { + %admin = %cl; + break; + } + } + return %admin; +} + +function serverCmdSetPDAPose(%client, %val) +{ + if(!isObject(%client.player)) + return; + + // if client is in a vehicle, return + if(%client.player.isMounted()) + return; + + if(%val) + { + // play "PDA" animation thread on player + %client.player.setActionThread("PDA", false); + } + else + { + // cancel PDA animation thread + %client.player.setActionThread("root", true); + } +} + +function serverCmdProcessGameLink(%client, %arg1, %arg2, %arg3, %arg4, %arg5) +{ + Game.processGameLink(%client, %arg1, %arg2, %arg3, %arg4, %arg5); +} + +//----------------------------------------------------------------------------------- +// z0dd - ZOD, 6/03/02. New function. Impact hit sounds settings from clientprefs.cs. +function serverCmdSetHitSounds( %client, %playerHitsOn, %playerHitWav, %vehicleHitsOn, %vehicleHitWav ) +{ + %client.playerHitSound = %playerHitsOn; + %client.playerHitWav = addtaggedString(%playerHitWav); + + %client.vehicleHitSound = %vehicleHitsOn; + %client.vehicleHitWav = addtaggedString(%vehicleHitWav); +} + +//----------------------------------------------------------------------------------- +// z0dd - ZOD, 6/03/02. New function. Get mod name from server. +function serverCMDgetMod(%client) +{ + %paths = getModPaths(); + commandToClient(%client, 'serverMod', %paths); +} + +//----------------------------------------------------------------------------------- +// z0dd - ZOD, 10/03/02. New function. Admin HUD print feature +function serverCMDaprint(%client, %msg, %bottom) +{ + if(%client.isAdmin) + { + %name = getTaggedString(%client.name); + %message = %name @ ": " @ %msg; + if(%bottom) + bottomprintAll(%message, 8, 3); + else + centerprintAll(%message, 8, 3); + } +} + +//----------------------------------------------------------------------------------- +// z0dd - ZOD - Canadian, 7/17/03. New functions. Allow clients to change their clan tags via hud. +function serverCmdcanGetClanTags(%client) +{ + if(isObject(%client) && !%client.isAiControlled()) + { + if(%client.GotTagList == 1) + return; + + %authInfo = %client.getAuthInfo(); + %numTags = getField( %authInfo, 4 ); + for(%i = 1; %i <= %numTags; %i++) + { + %client.clanTags = %client.clanTags TAB getField( %authInfo, 6 * %i); + } + %client.GotTagList = 1; + commandToClient(%client, 'canDisplayTags', %client.clanTags, %numTags); + } +} + +function serverCmdcanUpdateClanTag(%client, %tag) +{ + if(isObject(%client) && !%client.isAiControlled()) + { + if(!%client.isTagWaiting) + { + %client.isTagWaiting = true; + %client.tagWaitStart = getSimTime(); + %client.schedule(30000, ResetTagSwitchWait); + + %authInfo = %client.getAuthInfo(); + %numTags = getField( %authInfo, 4 ); + %rawname = getField( %authInfo, 0 ); + %found = 0; + for(%i = 1; %i <= %numTags; %i++) + { + if(getField(%authInfo, 6 * %i) $= %tag) + { + %newTag = getField(%authInfo, 6 * %i); + %append = getField(%authInfo, (6 * %i)+1 ); + %found = 1; + break; + } + } + if(!%found) + messageClient(%client, 'MsgError', 'Illegal clan tag, no changes made.'); + + if ( %append ) + { + %name = "\cp\c6" @ %rawname @ "\c7" @ %newTag @ "\co"; + %newname = "" @ %rawname @ "" @ %newTag; + } + else + { + %name = "\cp\c7" @ %newTag @ "\c6" @ %rawname @ "\co"; + %newname = "" @ %newTag @ "" @ %rawname; + } + MessageAll( 'MsgClientNameChanged', "", %client.name, %name, %client ); + removeTaggedString(%client.name); + %client.name = addTaggedString(%name); + setTargetName(%client.target, %client.name); + if(%client.team != 0) + Bottomprint( %client, "Your new name is " @ %newname , 5 ,1 ); + else + messageClient( %client, "", 'Your new name is %1', stripTaggedVar(%newname) ); + } + else + { + %wait = mFloor((30000 - (getSimTime() - %client.tagWaitStart)) / 1000); + messageClient(%client, "", '\c3WAIT MESSAGE:\cr You must wait another %1 seconds', %wait); + } + } +} + +function GameConnection::ResetTagSwitchWait(%this) +{ + %this.isTagWaiting = false; +} diff --git a/vehicles/vehicle.cs b/vehicles/vehicle.cs new file mode 100644 index 0000000..e81ce7c --- /dev/null +++ b/vehicles/vehicle.cs @@ -0,0 +1,1698 @@ +// Notes: +// - respawning vehicles with turrets (bomber/tank) will not setup the turret properly + +//Damage Rate for entering Liquid +$VehicleDamageLava = 0.0325; +$VehicleDamageHotLava = 0.0325; +$VehicleDamageCrustyLava = 0.0325; + +$NumVehiclesDeploy = 0; + +//************************************************************** +//* GENERAL PURPOSE FUNCTIONS +//************************************************************** + +function VehicleData::onAdd(%data, %obj) +{ + Parent::onAdd(%data, %obj); + if((%data.sensorData !$= "") && (%obj.getTarget() != -1)) + setTargetSensorData(%obj.getTarget(), %data.sensorData); + + %obj.setRechargeRate(%data.rechargeRate); + // set full energy + %obj.setEnergyLevel(%data.MaxEnergy); + + if(%obj.disableMove) + %obj.immobilized = true; + + if(%obj.deployed) + { + // z0dd - ZOD, 5/12/04. This is retarded. This causes many problems in Siege and + // Tournament mode for all game types. Deploy these vehicles regardless. + //if($countDownStarted) + // %data.schedule(($Host::WarmupTime * 1000) / 2, "vehicleDeploy", %obj, 0, 1); + //else + //{ + // $VehiclesDeploy[$NumVehiclesDeploy] = %obj; + // $NumVehiclesDeploy++; + //} + if($Host::WarmupTime >= 2) + %data.schedule(($Host::WarmupTime * 1000) / 2, "vehicleDeploy", %obj, 0, 1); + else + %data.schedule(2000, "vehicleDeploy", %obj, 0, 1); + } + if(%obj.mountable || %obj.mountable $= "") + %data.isMountable(%obj, true); + else + %data.isMountable(%obj, false); + + %obj.setSelfPowered(); +// %data.canObserve = true; +} + +function VehicleData::onRemove(%this, %obj) +{ + // if there are passengers/driver, kick them out + %this.deleteAllMounted(%obj); + for(%i = 0; %i < %obj.getDatablock().numMountPoints; %i++) + if (%obj.getMountNodeObject(%i)) { + %passenger = %obj.getMountNodeObject(%i); + %passenger.unmount(); + } + vehicleListRemove(%obj.getDataBlock(), %obj); + if(%obj.lastPilot.lastVehicle == %obj) + %obj.lastPilot.lastVehicle = ""; + + Parent::onRemove(%this, %obj); +} + +// z0dd - ZOD, 5/17/03. New function. Unlimited energy bug fix. +function MobileBaseVehicle::onRemove(%this, %obj) +{ + if(%obj.station.triggeredBy !$= "") + %obj.station.getDataBlock().onLeaveTrigger(%obj.station.trigger, %obj.station.triggeredBy); + + %obj.station.notDeployed = true; + Parent::onRemove(%this, %obj); +} + +// z0dd - ZOD, 4/10/04. ilys - checkSpeed function. Kill the driver and MPB if going too fast. +function MobileBaseVehicle::checkSpeed(%data, %obj) +{ + if(!isObject(%obj)) + return; + + if(isEventPending(%obj.speedCheck)) + cancel(%obj.speedCheck); + + if(VectorLen(%obj.getVelocity()) > 200) + { + if(%obj.getMountNodeObject(0)) %obj.getMountNodeObject(0).scriptKill(36); + %obj.setDamagelevel(4); + } + %obj.speedCheck = %data.schedule(5000, "checkSpeed", %obj); +} + +function VehicleData::onDamage(%this,%obj) +{ + %damage = %obj.getDamageLevel(); + if (%damage >= %this.destroyedLevel) + { + if(%obj.getDamageState() !$= "Destroyed") + { + if(%obj.respawnTime !$= "") + %obj.marker.schedule = %obj.marker.data.schedule(%obj.respawnTime, "respawn", %obj.marker); + %obj.setDamageState(Destroyed); + } + } + else + { + if(%obj.getDamageState() !$= "Enabled") + %obj.setDamageState(Enabled); + } +} + +function VehicleData::playerDismounted(%data, %obj, %player) +{ + if( %player.client.observeCount > 0 ) + resetObserveFollow( %player.client, true ); + + setTargetSensorGroup(%obj.getTarget(), %obj.team); + + // if there is a turret, set its team as well. + if( %obj.turretObject > 0 ) + setTargetSensorGroup(%obj.turretObject.getTarget(), %obj.team); +} + +function HoverVehicle::useCreateHeight() +{ + //this function is declared to prevent console error msg spam... +} + +function WheeledVehicle::useCreateHeight() +{ + //this function is declared to prevent console error msg spam... +} + +function AssaultVehicle::onDamage(%this, %obj) +{ + if(isObject(%obj.getMountNodeObject(10))) + (%obj.getMountNodeObject(10)).setDamagelevel(%obj.getDamageLevel()); + Parent::onDamage(%this, %obj); +} + +function BomberFlyer::onDamage(%this, %obj) +{ + if(isObject(%obj.getMountNodeObject(10))) + (%obj.getMountNodeObject(10)).setDamagelevel(%obj.getDamageLevel()); + Parent::onDamage(%this, %obj); +} + +function MobileBaseVehicle::onDamage(%this, %obj) +{ + if(isObject(%obj.getMountNodeObject(1))) + (%obj.getMountNodeObject(1)).setDamagelevel(%obj.getDamageLevel()); + Parent::onDamage(%this, %obj); +} + +function VehicleData::onEnterLiquid(%data, %obj, %coverage, %type) +{ + switch(%type) + { + case 0: + //Water + %obj.setHeat(0.0); + case 1: + //Ocean Water + %obj.setHeat(0.0); + case 2: + //River Water + %obj.setHeat(0.0); + case 3: + //Stagnant Water + %obj.setHeat(0.0); + case 4: + //Lava + %obj.liquidDamage(%data, $VehicleDamageLava, $DamageType::Lava); + case 5: + //Hot Lava + %obj.liquidDamage(%data, $VehicleDamageHotLava, $DamageType::Lava); + case 6: + //Crusty Lava + %obj.liquidDamage(%data, $VehicleDamageCrustyLava, $DamageType::Lava); + case 7: + //Quick Sand + } +} + +function FlyingVehicle::liquidDamage(%obj, %data, %damageAmount, %damageType) +{ + if(%obj.getDamageState() !$= "Destroyed") + { + %data.damageObject(%obj, 0, "0 0 0", %damageAmount, %damageType); + %obj.lDamageSchedule = %obj.schedule(50, "liquidDamage", %data, %damageAmount, %damageType); + passengerLiquidDamage(%obj, %damageAmount, %damageType); + } + else + %obj.lDamageSchedule = ""; +} + +function WheeledVehicle::liquidDamage(%obj, %data, %damageAmount, %damageType) +{ + if(%obj.getDamageState() !$= "Destroyed") + { + %data.damageObject(%obj, 0, "0 0 0", %damageAmount, %damageType); + %obj.lDamageSchedule = %obj.schedule(50, "liquidDamage", %data, %damageAmount, %damageType); + passengerLiquidDamage(%obj, %damageAmount, %damageType); + } + else + %obj.lDamageSchedule = ""; +} + +function HoverVehicle::liquidDamage(%obj, %data, %damageAmount, %damageType) +{ + if(%obj.getDamageState() !$= "Destroyed") + { + %data.damageObject(%obj, 0, "0 0 0", %damageAmount, %damageType); + %obj.lDamageSchedule = %obj.schedule(50, "liquidDamage", %data, %damageAmount, %damageType); + passengerLiquidDamage(%obj, %damageAmount, %damageType); + } + else + %obj.lDamageSchedule = ""; +} + +function passengerLiquidDamage(%obj, %damageAmount, %damageType) +{ + for(%i = %num; %i < %obj.getDataBlock().numMountPoints; %i++) + if (%p = %obj.getMountNodeObject(%i)) + %p.liquidDamage(%p.getDatablock(), $DamageLava, $DamageType::Lava); +} + +function VehicleData::onLeaveLiquid(%data, %obj, %type) +{ + switch(%type) + { + case 0: + //Water + %obj.setHeat(1.0); + case 1: + //Ocean Water + %obj.setHeat(1.0); + case 2: + //River Water + %obj.setHeat(1.0); + case 3: + //Stagnant Water + %obj.setHeat(1.0); + case 4: + //Lava + case 5: + //Hot Lava + case 6: + //Crusty Lava + case 7: + //Quick Sand + } + + if(%obj.lDamageSchedule !$= "") + { + cancel(%obj.lDamageSchedule); + %obj.lDamageSchedule = ""; + } +} + +function VehicleData::onDestroyed(%data, %obj, %prevState) +{ + if(%obj.lastDamagedBy) + { + %destroyer = %obj.lastDamagedBy; + game.vehicleDestroyed(%obj, %destroyer); + //error("vehicleDestroyed( "@ %obj @", "@ %destroyer @")"); + } + + radiusVehicleExplosion(%data, %obj); + if(%obj.turretObject) + if(%obj.turretObject.getControllingClient()) + %obj.turretObject.getDataBlock().playerDismount(%obj.turretObject); + for(%i = 0; %i < %obj.getDatablock().numMountPoints; %i++) + { + if (%obj.getMountNodeObject(%i)) { + %flingee = %obj.getMountNodeObject(%i); + %flingee.getDataBlock().doDismount(%flingee, true); + %xVel = 250.0 - (getRandom() * 500.0); + %yVel = 250.0 - (getRandom() * 500.0); + %zVel = (getRandom() * 100.0) + 50.0; + %flingVel = %xVel @ " " @ %yVel @ " " @ %zVel; + %flingee.applyImpulse(%flingee.getTransform(), %flingVel); + echo("got player..." @ %flingee.getClassName()); + %flingee.damage(0, %obj.getPosition(), 0.4, $DamageType::Crash); + } + } + + %data.deleteAllMounted(%obj); + // ----------------------------------------------------------------------------------------- + // z0dd - ZOD - Czar, 6/24/02. Move this vehicle out of the way so nothing collides with it. + if(%data.getName() $="BomberFlyer" || %data.getName() $="MobileBaseVehicle" || %data.getName() $="AssaultVehicle") + { + // %obj.setFrozenState(true); + %obj.schedule(2000, "delete"); + //%data.schedule(500, 'onAvoidCollisions', %obj); + %obj.schedule(500, "setPosition", vectorAdd(%obj.getPosition(), "40 -27 10000")); + } + else + { + %obj.setFrozenState(true); + %obj.schedule(500, "delete"); + } + // ----------------------------------------------------------------------------------------- +} + +// ---------------------------------------------------------------------------------- +// z0dd - ZOD, 6/13/02. Move this vehicle out of the way so nothing collides with it. +function VehicleData::onAvoidCollisions(%data, %obj) +{ + // Get the current location of the vehicle and send it to the moon! + %transform = posFromTransform(%obj.getTransform()); + %position = getWord(%transform, 0) SPC getWord(%transform, 1) SPC (getWord(%transform, 2)+2000); + %rotation = getWord(%transform, 3) SPC getWord(%transform, 4) SPC getWord(%transform, 5) SPC getWord(%transform, 6); + %obj.setTransform(%position SPC %rotation); +} +// ---------------------------------------------------------------------------------- + +// z0dd - ZOD, 5/12/04. Damage vehicles that collide with armor +function VehicleData::onCollision( %data, %obj, %col ) +{ + // Keep vehicle ghost from harming players? + if(%obj.getDamageState() $= "Destroyed") + return; + + if(!isObject(%obj) || !isObject(%col)) + return; + + if($Host::ClassicLoadVRamChanges) + { + if(%col.getDataBlock().className $= "Armor") + { + %vel = %obj.getVelocity(); + %vecLen = vectorDot(%vel, vectorNormalize(%vel)); + if(%vecLen > %data.collDamageThresholdVel) + { + %data.damageObject(%obj, %col, VectorAdd(%vel, %obj.getPosition()), %vecLen * %data.collDamageMultiplier, $DamageType::Impact); + } + %obj.playAudio(0, %data.hardImpactSound); + } + } +} + +function radiusVehicleExplosion(%data, %vehicle) +{ + // this is a modified version of RadiusExplosion() from projectiles.cs + %position = %vehicle.getPosition(); + InitContainerRadiusSearch(%position, %data.explosionRadius, $TypeMasks::PlayerObjectType | + $TypeMasks::VehicleObjectType | + $TypeMasks::MoveableObjectType | + $TypeMasks::StaticShapeObjectType | + $TypeMasks::ForceFieldObjectType | + $TypeMasks::TurretObjectType | + $TypeMasks::ItemObjectType); + + %numTargets = 0; + while ((%targetObject = containerSearchNext()) != 0) + { + if(%targetObject == %vehicle) + continue; + + %dist = containerSearchCurrRadDamageDist(); + + if (%dist > %data.explosionRadius) + continue; + + if (%targetObject.isMounted()) + { + %mount = %targetObject.getObjectMount(); + if(%mount == %vehicle) + continue; + + %found = -1; + for (%i = 0; %i < %mount.getDataBlock().numMountPoints; %i++) + { + if (%mount.getMountNodeObject(%i) == %targetObject) + { + %found = %i; + break; + } + } + + if (%found != -1) + { + if (%mount.getDataBlock().isProtectedMountPoint[%found] && (%mount != %vehicle)) + continue; + } + } + + %targets[%numTargets] = %targetObject; + %targetDists[%numTargets] = %dist; + %numTargets++; + } + + for (%i = 0; %i < %numTargets; %i++) + { + %targetObject = %targets[%i]; + %dist = %targetDists[%i]; + + %coverage = calcExplosionCoverage(%position, %targetObject, + ($TypeMasks::InteriorObjectType | + $TypeMasks::TerrainObjectType | + $TypeMasks::ForceFieldObjectType)); + if (%coverage == 0) + continue; + + %amount = (1.0 - (%dist / %data.explosionRadius)) * %coverage * %data.explosionDamage; + %targetData = %targetObject.getDataBlock(); + + %momVec = "0 0 1"; + + if(%amount > 0) + %targetData.damageObject(%targetObject, %sourceObject, %position, %amount, $DamageType::Explosion, %momVec); + } +} + +function VehicleData::deleteAllMounted() +{ + +} + +//************************************************************** +//* VEHICLE CREATION +//************************************************************** +// (NOTE: No entry for Wildcat Grav Cycle is here. -- DG) + +//---------------------------- +// SHRIKE SCOUT FLIER +//---------------------------- + +function ScoutFlyer::onAdd(%this, %obj) +{ + Parent::onAdd(%this, %obj); + %obj.mountImage(ScoutChaingunParam, 0); + %obj.mountImage(ScoutChaingunImage, 2); + %obj.mountImage(ScoutChaingunPairImage, 3); + %obj.nextWeaponFire = 2; + %obj.schedule(5500, "playThread", $ActivateThread, "activate"); +} + +//---------------------------- +// THUNDERSWORD BOMBER +//---------------------------- + +function BomberFlyer::onAdd(%this, %obj) +{ + Parent::onAdd(%this, %obj); + + %turret = TurretData::create(BomberTurret); + MissionCleanup.add(%turret); + %turret.team = %obj.teamBought; + %turret.selectedWeapon = 1; + %turret.setSelfPowered(); + %obj.mountObject(%turret, 10); + %turret.mountImage(BomberTurretBarrel,2); + %turret.mountImage(BomberTurretBarrelPair,3); + %turret.mountImage(BomberBombImage, 4); + %turret.mountImage(BomberBombPairImage, 5); + %turret.mountImage(BomberTargetingImage, 6); + %obj.turretObject = %turret; + %turret.setCapacitorRechargeRate( %turret.getDataBlock().capacitorRechargeRate ); + %turret.vehicleMounted = %obj; + + //vehicle turrets should not auto fire at targets + %turret.setAutoFire(false); + + //for this particular weapon - a non-firing datablock used only so the AI can aim the turret + //Also needed so we can set the turret parameters.. + %turret.mountImage(AIAimingTurretBarrel, 0); + + // setup the turret's target info + setTargetSensorGroup(%turret.getTarget(), %turret.team); + setTargetNeverVisMask(%turret.getTarget(), 0xffffffff); +} + +//---------------------------- +// HAVOC TRANSPORT FLIER +//---------------------------- + +function HAPCFlyer::onAdd(%this, %obj) +{ + Parent::onAdd(%this, %obj); + %obj.schedule(6000, "playThread", $ActivateThread, "activate"); +} + +//---------------------------- +// BEOWULF ASSAULT VEHICLE +//---------------------------- + +function AssaultVehicle::onAdd(%this, %obj) +{ + Parent::onAdd(%this, %obj); + + %turret = TurretData::create(AssaultPlasmaTurret); + %turret.selectedWeapon = 1; + MissionCleanup.add(%turret); + %turret.team = %obj.teamBought; + %turret.setSelfPowered(); + %obj.mountObject(%turret, 10); + %turret.mountImage(AssaultPlasmaTurretBarrel, 2); + %turret.mountImage(AssaultMortarTurretBarrel, 4); + %turret.setCapacitorRechargeRate( %turret.getDataBlock().capacitorRechargeRate ); + %obj.turretObject = %turret; + + //vehicle turrets should not auto fire at targets + %turret.setAutoFire(false); + + //Needed so we can set the turret parameters.. + %turret.mountImage(AssaultTurretParam, 0); + %obj.schedule(6000, "playThread", $ActivateThread, "activate"); + + // set the turret's target info + setTargetSensorGroup(%turret.getTarget(), %turret.team); + setTargetNeverVisMask(%turret.getTarget(), 0xffffffff); +} + +//---------------------------- +// JERICHO FORWARD BASE (Mobile Point Base) +//---------------------------- + +function MobileBaseVehicle::onAdd(%this, %obj) +{ + Parent::onAdd(%this, %obj); + %obj.station = ""; + %obj.turret = ""; + %obj.beacon = ""; + + %obj.schedule(5000, "playThread", $AmbientThread, "ambient"); + + // z0dd - ZOD, 4/10/04. ilys - Start checkSpeed schedule on MPB + %this.checkSpeed(%obj); +} + +//************************************************************** +//* MULTI-CREW VEHICLE DELETION +//************************************************************** + +//---------------------------- +//BEOWULF ASSAULT VEHICLE +//---------------------------- + +function AssaultVehicle::deleteAllMounted(%data, %obj) +{ + %turret = %obj.getMountNodeObject(10); + if(!isObject(%turret)) + return; + + if(%client = %turret.getControllingClient()) + { + %client.player.setControlObject(%client.player); + %client.player.mountImage(%client.player.lastWeapon, $WeaponSlot); + %client.player.mountVehicle = false; + } + %turret.schedule(2000, "delete"); +} + +//---------------------------- +// THUNDERSWORD BOMBER +//---------------------------- + +function BomberFlyer::deleteAllMounted(%data, %obj) +{ + if(isObject(%obj.beacon)) + %obj.beacon.schedule(50, delete); + + %turret = %obj.getMountNodeObject(10); + if(!%turret) + return; + + %turret.altTrigger = 0; + %turret.fireTrigger = 0; + + if(%client = %turret.getControllingClient()) + { + commandToClient(%client, 'endBomberSight'); + %client.player.setControlObject(%client.player); + %client.player.mountImage(%client.player.lastWeapon, $WeaponSlot); + %client.player.mountVehicle = false; + + %client.player.bomber = false; + %client.player.isBomber = false; + } + %turret.schedule(2000, delete); +} + +//---------------------------- +// JERICHO FORWARD BASE +//---------------------------- + +function MobileBaseVehicle::deleteAllMounted(%data, %obj) +{ + if(isEventPending(%obj.speedCheck)) + cancel(%obj.speedCheck); + + if(isObject(%obj.station)) // z0dd - ZOD, 4/25/02. Console spam fix. + { + if(%obj.station.triggeredBy !$= "") + %obj.station.getDataBlock().onLeaveTrigger(%obj.station.trigger, %obj.station.triggeredBy); + + %obj.station.notDeployed = true; + %obj.station.getDataBlock().onLosePowerDisabled(%obj.station); + %obj.unmountObject(%obj.station); + %obj.station.trigger.schedule(1000, delete); + %obj.station.schedule(2000, delete); + } + if(isObject(%obj.turret)) // z0dd - ZOD, 4/25/02. Console spam fix. + { + %obj.turret.getDataBlock().onLosePowerDisabled(%obj.turret); + %obj.unmountObject(%obj.turret); + %obj.turret.schedule(2000, delete); + } + //-------------------------------------------------------------------------- + // z0dd - ZOD, 4/25/02. MPB Teleporter. +// if (isObject(%obj.teleporter)) +// { +// %obj.teleporter.setThreadDir($ActivateThread, FALSE); +// %obj.teleporter.playThread($ActivateThread,"activate"); +// %obj.teleporter.playAudio($ActivateSound, StationVehicleDeactivateSound); +// } + //-------------------------------------------------------------------------- + if(isObject(%obj.shield)) + %obj.shield.schedule(2000, delete); + + if(isObject(%obj.beacon)) + { + %obj.beacon.schedule(0, delete); + } +} + +//************************************************************** +//* WEAPON MOUNTING ON VEHICLES +//************************************************************** + +//---------------------------- +// SHRIKE SCOUT FLIER +//---------------------------- + +function ScoutFlyer::playerMounted(%data, %obj, %player, %node) +{ + // scout flyer == SUV (single-user vehicle) + commandToClient(%player.client, 'setHudMode', 'Pilot', "Shrike", %node); + $numVWeapons = 1; + + // update observers who are following this guy... + if( %player.client.observeCount > 0 ) + resetObserveFollow( %player.client, false ); +} + +//---------------------------- +// THUNDERSWORD BOMBER +//---------------------------- + +function BomberFlyer::playerMounted(%data, %obj, %player, %node) +{ + if(%node == 0) + { + // pilot position + %player.setPilot(true); + commandToClient(%player.client, 'setHudMode', 'Pilot', "Bomber", %node); + } + else if(%node == 1) + { + // bombardier position + %turret = %obj.getMountNodeObject(10); + %player.vehicleTurret = %turret; + %player.setTransform("0 0 0 0 0 1 0"); + %player.lastWeapon = %player.getMountedImage($WeaponSlot); + %player.unmountImage($WeaponSlot); + if(!%player.client.isAIControlled()) + { + %player.setControlObject(%turret); + %player.client.setObjectActiveImage(%turret, 2); + } + commandToClient(%player.client, 'startBomberSight'); + %turret.bomber = %player; + $bWeaponActive = 0; + %obj.getMountNodeObject(10).selectedWeapon = 1; + commandToClient(%player.client,'SetWeaponryVehicleKeys', true); + + commandToClient(%player.client, 'setHudMode', 'Pilot', "Bomber", %node); + %player.isBomber = true; + } + else + { + // tail gunner position + commandToClient(%player.client, 'setHudMode', 'Passenger', "Bomber", %node); + } + // build a space-separated string representing passengers + // 0 = no passenger; 1 = passenger (e.g. "1 0 0 ") + %passString = buildPassengerString(%obj); + // send the string of passengers to all mounted players + for(%i = 0; %i < %data.numMountPoints; %i++) + if(%obj.getMountNodeObject(%i) > 0) + commandToClient(%obj.getMountNodeObject(%i).client, 'checkPassengers', %passString); + + // update observers who are following this guy... + if( %player.client.observeCount > 0 ) + resetObserveFollow( %player.client, false ); +} + +//---------------------------- +// HAVOC TRANSPORT FLIER +//---------------------------- + +function HAPCFlyer::playerMounted(%data, %obj, %player, %node) +{ + if(%node == 0) { + // pilot position + commandToClient(%player.client, 'setHudMode', 'Pilot', "HAPC", %node); + } + else { + // all others + commandToClient(%player.client, 'setHudMode', 'Passenger', "HAPC", %node); + } + // build a space-separated string representing passengers + // 0 = no passenger; 1 = passenger (e.g. "1 0 0 1 1 0 ") + %passString = buildPassengerString(%obj); + // send the string of passengers to all mounted players + for(%i = 0; %i < %data.numMountPoints; %i++) + if(%obj.getMountNodeObject(%i) > 0) + commandToClient(%obj.getMountNodeObject(%i).client, 'checkPassengers', %passString); + + // update observers who are following this guy... + if( %player.client.observeCount > 0 ) + resetObserveFollow( %player.client, false ); +} + +//---------------------------- +// WILDCAT GRAV CYCLE +//---------------------------- + +function ScoutVehicle::playerMounted(%data, %obj, %player, %node) +{ + // scout vehicle == SUV (single-user vehicle) + commandToClient(%player.client, 'setHudMode', 'Pilot', "Hoverbike", %node); + + // update observers who are following this guy... + if( %player.client.observeCount > 0 ) + resetObserveFollow( %player.client, false ); +} + +//---------------------------- +// BEOWULF ASSAULT VEHICLE +//---------------------------- + +function AssaultVehicle::playerMounted(%data, %obj, %player, %node) +{ + if(%node == 0) { + // driver position + // is there someone manning the turret? + //%turreteer = %obj.getMountedNodeObject(1); + commandToClient(%player.client, 'setHudMode', 'Pilot', "Assault", %node); + } + else if(%node == 1) + { + // turreteer position + %turret = %obj.getMountNodeObject(10); + %player.vehicleTurret = %turret; + %player.setTransform("0 0 0 0 0 1 0"); + %player.lastWeapon = %player.getMountedImage($WeaponSlot); + %player.unmountImage($WeaponSlot); + if(!%player.client.isAIControlled()) + { + %player.setControlObject(%turret); + %player.client.setObjectActiveImage(%turret, 2); + } + %turret.turreteer = %player; + // if the player is the turreteer, show vehicle's weapon icons + //commandToClient(%player.client, 'showVehicleWeapons', %data.getName()); + //%player.client.setVWeaponsHudActive(1); // plasma turret icon (default) + + $aWeaponActive = 0; + commandToClient(%player.client,'SetWeaponryVehicleKeys', true); + %obj.getMountNodeObject(10).selectedWeapon = 1; + commandToClient(%player.client, 'setHudMode', 'Pilot', "Assault", %node); + } + + // update observers who are following this guy... + if( %player.client.observeCount > 0 ) + resetObserveFollow( %player.client, false ); + + // build a space-separated string representing passengers + // 0 = no passenger; 1 = passenger (e.g. "1 0 ") + %passString = buildPassengerString(%obj); + // send the string of passengers to all mounted players + for(%i = 0; %i < %data.numMountPoints; %i++) + if(%obj.getMountNodeObject(%i) > 0) + commandToClient(%obj.getMountNodeObject(%i).client, 'checkPassengers', %passString); +} + +//---------------------------- +// JERICHO FORWARD BASE +//---------------------------- + +function MobileBaseVehicle::playerMounted(%data, %obj, %player, %node) +{ + // MPB vehicle == SUV (single-user vehicle) + commandToClient(%player.client, 'setHudMode', 'Pilot', "MPB", %node); + if(%obj.deploySchedule) + { + %obj.deploySchedule.clear(); + %obj.deploySchedule = ""; + } + + if(%obj.deployed !$= "" && %obj.deployed == 1) + { + %obj.setThreadDir($DeployThread, false); + %obj.playThread($DeployThread,"deploy"); + %obj.playAudio($DeploySound, MobileBaseUndeploySound); + %obj.station.setThreadDir($DeployThread, false); + %obj.station.getDataBlock().onLosePowerDisabled(%obj.station); + %obj.station.clearSelfPowered(); + %obj.station.goingOut=false; + %obj.station.notDeployed = 1; + %obj.station.playAudio($DeploySound, MobileBaseStationUndeploySound); + + if((%turretClient = %obj.turret.getControllingClient()) !$= "") + { + CommandToServer( 'resetControlObject', %turretClient ); + } + + %obj.turret.setThreadDir($DeployThread, false); + %obj.turret.clearTarget(); + %obj.turret.setTargetObject(-1); + + %obj.turret.playAudio($DeploySound, MobileBaseTurretUndeploySound); + %obj.shield.open(); + %obj.shield.schedule(1000,"delete"); + %obj.deploySchedule = ""; + //----------------------------------------------------------------------- + // z0dd - ZOD, 4/25/02. MPB Teleporter. +// %obj.teleporter.setThreadDir($ActivateThread, FALSE); +// %obj.teleporter.playThread($ActivateThread,"activate"); +// %obj.teleporter.playAudio($ActivateSound, StationVehicleDeactivateSound); + //----------------------------------------------------------------------- + %obj.fullyDeployed = 0; + + %obj.noEnemyControl = 0; + } + %obj.deployed = 0; + + // update observers who are following this guy... + if( %player.client.observeCount > 0 ) + resetObserveFollow( %player.client, false ); +} + +function buildPassengerString(%vehicle) +{ + %passStr = ""; + for(%i = 0; %i < %vehicle.getDatablock().numMountPoints; %i++) + { + if(%vehicle.getMountNodeObject(%i) > 0) + %passStr = %passStr @ "1 "; + else + %passStr = %passStr @ "0 "; + } + + return %passStr; +} + +function MobileBaseVehicle::playerDismounted(%data, %obj, %player) +{ + %obj.schedule(500, "deployVehicle", %data, %player); + Parent::playerDismounted( %data, %obj, %player ); +} + +function WheeledVehicle::deployVehicle(%obj, %data, %player) +{ + if (!%data.vehicleDeploy(%obj, %player)) + %obj.schedule(500, "deployVehicle", %data, %player); +} + +//************************************************************** +//* JERICHO DEPLOYMENT and UNDEPLOYMENT +//************************************************************** + +function MobileBaseVehicle::vehicleDeploy(%data, %obj, %player, %force) +{ + if(VectorLen(%obj.getVelocity()) <= 0.1 || %force) + { + %deployMessage = ""; + if( (%deployMessage = %data.checkTurretDistance(%obj)) $= "" || %force) + { + if(%obj.station $= "") + { + if( (%deployMessage = %data.checkDeploy(%obj)) $= "" || %force) + { + %obj.station = new StaticShape() { + scale = "1 1 1"; + dataBlock = "MobileInvStation"; + lockCount = "0"; + homingCount = "0"; + team = %obj.team; + vehicle = %obj; + }; + %obj.station.startFade(0,0,true); + %obj.mountObject(%obj.station, 2); + %obj.station.getDataBlock().createTrigger(%obj.station); + %obj.station.setSelfPowered(); + %obj.station.playThread($PowerThread,"Power"); + %obj.station.playAudio($HumSound,StationHumSound); + %obj.station.vehicle = %obj; + %obj.turret = new turret() { + scale = "1 1 1"; + dataBlock = "MobileTurretBase"; + lockCount = "0"; + homingCount = "0"; + team = %obj.team; + }; + %obj.turret.setDamageLevel(%obj.getDamageLevel()); + %obj.mountObject(%obj.turret, 1); + %obj.turret.setSelfPowered(); + %obj.turret.playThread($PowerThread,"Power"); + + //%obj.turret.mountImage(MissileBarrelLarge, 0 ,false); + // ----------------------------------------------------- + // z0dd - ZOD, 4/25/02. modular MPB turret + if(%obj.barrel !$= "") + { + %obj.turret.mountImage(%obj.barrel, 0 ,false); + } + else + { + %obj.turret.mountImage(MissileBarrelLarge, 0 ,false); + } + // ----------------------------------------------------- + + %obj.beacon = new BeaconObject() { + dataBlock = "DeployedBeacon"; + position = %obj.position; + rotation = %obj.rotation; + team = %obj.team; + }; + %obj.beacon.setBeaconType(friend); + %obj.beacon.setTarget(%obj.team); + + checkSpawnPos(%obj, 20); + } + } + else + { + %obj.station.setSelfPowered(); + %obj.station.playThread($PowerThread,"Power"); + %obj.turret.setSelfPowered(); + %obj.turret.playThread($PowerThread,"Power"); + } + if(%deployMessage $= "" || %force) + { + if(%obj.turret.getTarget() == -1) + { + %obj.turret.setTarget(%obj.turret.target); + } + %obj.turret.setThreadDir($DeployThread, true); + %obj.turret.playThread($DeployThread,"deploy"); + %obj.turret.playAudio($DeploySound, MobileBaseTurretDeploySound); + + %obj.station.notDeployed = 1; + %obj.setThreadDir($DeployThread, true); + %obj.playThread($DeployThread,"deploy"); + %obj.playAudio($DeploySound, MobileBaseDeploySound); + %obj.deployed = 1; + %obj.deploySchedule = ""; + %obj.disableMove = true; + %obj.setFrozenState(true); + if(isObject(%obj.shield)) + %obj.shield.delete(); + + %obj.shield = new forceFieldBare() + { + scale = "1.22 1.8 1.1"; + dataBlock = "defaultTeamSlowFieldBare"; + team = %obj.team; + }; + %obj.shield.open(); + setTargetSensorData(%obj.getTarget(), MPBDeployedSensor); + } + } + if(%deployMessage !$= "") + messageClient(%player.client, '', %deployMessage); + + return true; + } + else + { + return false; + } +} + +function MobileBaseVehicle::onEndSequence(%data, %obj, %thread) +{ + if(%thread == $DeployThread && !%obj.deployed) + { + %obj.unmountObject(%obj.station); + %obj.station.trigger.delete(); + %obj.station.delete(); + %obj.station = ""; + + %obj.beacon.delete(); + + %obj.unmountObject(%obj.turret); + %obj.turret.delete(); + %obj.turret = ""; + + if(!%obj.immobilized) + { + %obj.disableMove = false; + %obj.setFrozenState(false); + } + setTargetSensorData(%obj.getTarget(), %data.sensorData); + } + else + { + %obj.station.startFade(0,0,false); + %obj.station.setThreadDir($DeployThread, true); + %obj.station.playThread($DeployThread,"deploy"); + %obj.station.playAudio($DeploySound, MobileBaseStationDeploySound); + %obj.station.goingOut = true; + %obj.shield.setTransform(%obj.getSlotTransform(3)); + %obj.shield.close(); + %obj.isDeployed = true; + %obj.noEnemyControl = 1; + } + + Parent::onEndSequence(%data, %obj, %thread); +} + +function MobileInvStation::onEndSequence(%data, %obj, %thread) +{ + if(!%obj.goingOut) + %obj.startFade(0,0,true); + else + { + %obj.notDeployed = 0; + %obj.vehicle.fullyDeployed = 1; + //-------------------------------------------------------------------------------- + // z0dd - ZOD, 4/25/02. MPB Teleporter. +// if(isObject(%obj.vehicle.teleporter)) +// { +// %obj.vehicle.teleporter.setThreadDir($ActivateThread, TRUE); +// %obj.vehicle.teleporter.playThread($ActivateThread,"activate"); +// %obj.vehicle.teleporter.playAudio($ActivateSound, StationVehicleAcitvateSound); +// } + //-------------------------------------------------------------------------------- + } + Parent::onEndSequence(%data, %obj, %thread); +} + +function MobileBaseVehicle::checkDeploy(%data, %obj) +{ + %mask = $TypeMasks::VehicleObjectType | $TypeMasks::MoveableObjectType | + $TypeMasks::StaticShapeObjectType | $TypeMasks::ForceFieldObjectType | + $TypeMasks::ItemObjectType | $TypeMasks::PlayerObjectType | + $TypeMasks::TurretObjectType | //$TypeMasks::StaticTSObjectType | + $TypeMasks::InteriorObjectType; + + //%slot 1 = turret %slot 2 = station + %height[1] = 0; + %height[2] = 0; + %radius[1] = 2.4; + %radius[2] = 2.4; + %stationFailed = false; + %turretFailed = false; + + for(%x = 1; %x < 3; %x++) + { + %posXY = getWords(%obj.getSlotTransform(%x), 0, 1); + %posZ = (getWord(%obj.getSlotTransform(%x), 2) + %height[%x]); + InitContainerRadiusSearch(%posXY @ " " @ %posZ, %radius[%x], %mask); + + while ((%objFound = ContainerSearchNext()) != 0) + { + if(%objFound != %obj) + { + if(%x == 1) + %turretFailed = true; + else + %stationFailed = true; + break; + } + } + } + + //If turret, station or both fail the send back the error message... + if(%turretFailed && %stationFailed) + return "Both Turret and Station are blocked and unable to deploy."; + if(%turretFailed) + return "Turret is blocked and unable to deploy."; + if(%stationFailed) + return "Station is blocked and unable to deploy."; + + //Check the station for collision with the Terrain + %mat = %obj.getTransform(); + for(%x = 1; %x < 7; %x+=2) + { + %startPos = MatrixMulPoint(%mat, %data.stationPoints[%x]); + %endPos = MatrixMulPoint(%mat, %data.stationPoints[%x+1]); + + %rayCastObj = containerRayCast(%startPos, %endPos, $TypeMasks::TerrainObjectType, 0); + if(%rayCastObj) + return "Station is blocked by terrain and unable to deploy."; + } + + return ""; +} +function MobileBaseVehicle::checkTurretDistance(%data, %obj) +{ + %pos = getWords(%obj.getTransform(), 0, 2); + InitContainerRadiusSearch(%pos, 20, $TypeMasks::TurretObjectType | $TypeMasks::InteriorObjectType); // z0dd - ZOD, 6/21/02. Allow closer deploy. Was 100 + while ((%objFound = ContainerSearchNext()) != 0) + { + if(%objFound.getType() & $TypeMasks::TurretObjectType) + { + if(%objFound.getDataBlock().ClassName $= "TurretBase") + return "Turret Base is in the area. Unable to deploy."; + } + else + { + %subStr = getSubStr(%objFound.interiorFile, 1, 4); + if(%subStr !$= "rock" && %subStr !$= "spir" && %subStr !$= "misc") + return "Building is in the area. Unable to deploy."; + } + } + return ""; +} + + +//************************************************************** +//* VEHICLE INVENTORY MANAGEMENT +//************************************************************** + +//-------------------------------------------------------------- +// NUMBER OF PURCHASEABLE VEHICLES PER TEAM +//-------------------------------------------------------------- + +$VehicleRespawnTime = 15000; +$Vehiclemax[ScoutVehicle] = 4; +$VehicleMax[AssaultVehicle] = 3; +$VehicleMax[MobileBaseVehicle] = 1; +$VehicleMax[ScoutFlyer] = 4; +$VehicleMax[BomberFlyer] = 2; +$VehicleMax[HAPCFlyer] = 2; + +function vehicleListRemove(%data, %obj) +{ + %blockName = %data.getName(); + for($i = 0; %i < $VehicleMax[%blockName]; %i++) + if($VehicleInField[%obj.team, %blockName, %i] == %obj) + { + $VehicleInField[%obj.team, %blockName, %i] = 0; + $VehicleTotalCount[%obj.team, %blockName]--; + break; + } +} + +function vehicleListAdd(%blockName, %obj) +{ + for($i = 0; %i < $VehicleMax[%blockName]; %i++) + { + if($VehicleInField[%obj.team, %blockName, %i] $= "" || $VehicleInField[%obj.team, %blockName, %i] == 0) + { + $VehicleInField[%obj.team, %blockName, %i] = %obj; + $VehicleTotalCount[%obj.team, %blockName]++; + break; + } + } +} + +function clearVehicleCount(%team) +{ + $VehicleTotalCount[%team, ScoutVehicle] = 0; + $VehicleTotalCount[%team, AssaultVehicle] = 0; + $VehicleTotalCount[%team, MobileBaseVehicle] = 0; + $VehicleTotalCount[%team, ScoutFlyer] = 0; + $VehicleTotalCount[%team, BomberFlyer] = 0; + $VehicleTotalCount[%team, HAPCFlyer] = 0; +} + +//************************************************************** +//* VEHICLE HUD SEAT INDICATOR LIGHTS +//************************************************************** + +// --------------------------------------------------------- +// z0dd - ZOD, 6/18/02. Get the name of the vehicle node and +// pass to Armor::onMount and Armor::onDismount in player.cs +function findNodeName(%vehicle, %node) +{ + %vName = %vehicle.getDataBlock().getName(); + if(%vName !$= "HAPCFlyer") + { + if(%node == 0) + return 'pilot'; + else if(%node == 1) + return 'gunner'; + else + return 'tailgunner'; + } + else + { + if(%node == 0) + return 'pilot'; + else if(%node == 1) + return 'tailgunner'; + else + return 'passenger'; + } +} + +// z0dd - ZOD, 4/10/04. Lagg_Alot, re-write. +function findAIEmptySeat(%vehicle, %player) +{ + %myArmor = %player.getArmorSize(); + %dataBlock = %vehicle.getDataBlock(); + if (%dataBlock.getName() $= "BomberFlyer" && %myArmor $= "Heavy") + %num = 2; + else + %num = 1; + + %node = -1; + for(%i = %num; %i < %dataBlock.numMountPoints; %i++) + { + if (!%vehicle.getMountNodeObject(%i)) + { + //cheap hack - for now, AI's will mount the next available node regardless of where they collided + %node = %i; + break; + } + } + //return the empty seat + return %node; +} + +function findEmptySeat(%vehicle, %player, %forceNode) +{ + %minNode = 1; + %node = -1; + %dataBlock = %vehicle.getDataBlock(); + %dis = %dataBlock.minMountDist; + %playerPos = getWords(%player.getTransform(), 0, 2); + %message = ""; + if(%dataBlock.lightOnly) + { + if(%player.client.armor $= "Light") + %minNode = 0; + else + %message = '\c2Only Scout Armors can pilot this vehicle.~wfx/misc/misc.error.wav'; + } + else if(%player.client.armor $= "Light" || %player.client.armor $= "Medium") + %minNode = 0; + else + %minNode = findFirstHeavyNode(%dataBlock); + + if(%forceNode !$= "") + %node = %forceNode; + else + { + for(%i = 0; %i < %dataBlock.numMountPoints; %i++) + if(!%vehicle.getMountNodeObject(%i)) + { + %seatPos = getWords(%vehicle.getSlotTransform(%i), 0, 2); + %disTemp = VectorLen(VectorSub(%seatPos, %playerPos)); + if(%disTemp <= %dis) + { + %node = %i; + %dis = %disTemp; + } + } + } + if(%node != -1 && %node < %minNode) + { + if(%message $= "") + { + if(%node == 0) + %message = '\c2Only Scout or Assault Armors can pilot this vehicle.~wfx/misc/misc.error.wav'; + else + %message = '\c2Only Scout or Assault Armors can use that position.~wfx/misc/misc.error.wav'; + } + + if(!%player.noSitMessage) + { + %player.noSitMessage = true; + %player.schedule(2000, "resetSitMessage"); + messageClient(%player.client, 'MsgArmorCantMountVehicle', %message); + } + %node = -1; + } + return %node; +} + +function findFirstHeavyNode(%data) +{ + for(%i = 0; %i < %data.numMountPoints; %i++) + if(%data.mountPose[%i] $= "") + return %i; + return %data.numMountPoints; +} + +//************************************************************** +//* DAMAGE FUNCTIONS +//************************************************************** + +function VehicleData::damageObject(%data, %targetObject, %sourceObject, %position, %amount, %damageType, %momVec, %theClient, %proj) +{ + if(%proj !$= "") + { + if(%amount > 0 && %targetObject.lastDamageProj !$= %proj) + { + %targetObject.lastDamageProj = %proj; + %targetObject.lastDamageAmount = %amount; + } + else if(%targetObject.lastDamageAmount < %amount) + %amount = %amount - %targetObject.lastDamageAmount; + else + return; + } + + // check for team damage + %sourceClient = %sourceObject ? %sourceObject.getOwnerClient() : 0; + %targetTeam = getTargetSensorGroup(%targetObject.getTarget()); + + if(%sourceClient) + %sourceTeam = %sourceClient.getSensorGroup(); + else if(isObject(%sourceObject) && %sourceObject.getClassName() $= "Turret") + { + %sourceTeam = getTargetSensorGroup(%sourceObject.getTarget()); + %sourceClient = %sourceObject.getControllingClient(); // z0dd - ZOD, 6/10/02. Play a sound to client when they hit a vehicle with a controlled turret + } + else + { + %sourceTeam = %sourceObject ? getTargetSensorGroup(%sourceObject.getTarget()) : -1; + // Client is allready defined and this spams console - ZOD + //%sourceClient = %sourceObject.getControllingClient(); // z0dd - ZOD, 6/10/02. Play a sound to client when they hit a vehicle from a vehicle + } + + // vehicles no longer obey team damage -JR +// if(!$teamDamage && (%targetTeam == %sourceTeam) && %targetObject.getDamagePercent() > 0.5) +// return; + //but we do want to track the destroyer + if(%sourceObject) + { + %targetObject.lastDamagedBy = %sourceObject; + %targetObject.lastDamageType = %damageType; + } + else + %targetObject.lastDamagedBy = 0; + + // ---------------------------------------------------------------------------------- + // z0dd - ZOD, 6/10/02. Play a sound to client when they hit a vehicle + if(%sourceClient && %sourceClient.vehicleHitSound) + { + if(%targetTeam != %sourceTeam) + { + if ((%damageType > 0 && %damageType < 11) || + (%damageType == 13) || + (%damageType > 15 && %damageType < 24) || + (%damageType > 25 && %damageType < 32)) + { + messageClient(%sourceClient, 'MsgClientHit', %sourceClient.vehicleHitWav); + } + } + } + // ---------------------------------------------------------------------------------- + + // Scale damage type & include shield calculations... + if (%data.isShielded) + %amount = %data.checkShields(%targetObject, %position, %amount, %damageType); + + + %damageScale = %data.damageScale[%damageType]; + if(%damageScale !$= "") + %amount *= %damageScale; + + if(%amount != 0) + %targetObject.applyDamage(%amount); + + if(%targetObject.getDamageState() $= "Destroyed" ) + { + if( %momVec !$= "") + %targetObject.setMomentumVector(%momVec); + } +} + +function VehicleData::onImpact(%data, %vehicleObject, %collidedObject, %vec, %vecLen) +{ + if(%vecLen > %data.minImpactSpeed) + %data.damageObject(%vehicleObject, 0, VectorAdd(%vec, %vehicleObject.getPosition()), + %vecLen * %data.speedDamageScale, $DamageType::Ground); + + // associated "crash" sounds + if(%vecLen > %vDataBlock.hardImpactSpeed) + %vehicleObject.playAudio(0, %vDataBlock.hardImpactSound); + else if(%vecLen > %vDataBlock.softImpactSpeed) + %vehicleObject.playAudio(0, %vDataBlock.softImpactSound); +} + +//************************************************************** +//* VEHICLE TIMEOUTS +//************************************************************** + +function vehicleAbandonTimeOut(%vehicle) +{ + if(%vehicle.getDatablock().cantAbandon $= "" && %vehicle.lastPilot $= "") + { + for(%i = 0; %i < %vehicle.getDatablock().numMountPoints; %i++) + if (%vehicle.getMountNodeObject(%i)) + { + %passenger = %vehicle.getMountNodeObject(%i); + if(%passenger.lastVehicle !$= "") + schedule(15000, %passenger.lastVehicle,"vehicleAbandonTimeOut", %passenger.lastVehicle); + %passenger.lastVehicle = %vehicle; + %vehicle.lastPilot = %passenger; + return; + } + + if(%vehicle.respawnTime !$= "") + %vehicle.marker.schedule = %vehicle.marker.data.schedule(%vehicle.respawnTime, "respawn", %vehicle.marker); + %vehicle.mountable = 0; + %vehicle.startFade(1000, 0, true); + %vehicle.schedule(1001, "delete"); + } +} + +//------------------------------------------------------------------------------ +function HoverVehicleData::create(%block, %team, %oldObj) +{ + if(%oldObj $= "") + { + %obj = new HoverVehicle() + { + dataBlock = %block; + respawn = "0"; + teamBought = %team; + team = %team; + }; + } + else + { + %obj = new HoverVehicle() + { + dataBlock = %block; // z0dd - ZOD, 5/18/03. Dynamix had param named %data, screwed up creation + respawn = %oldObj.respawn; // z0dd - ZOD, 4/10/04. Fixed, was 0 + teamBought = %team; + team = %team; + mountable = %oldObj.mountable; + disableMove = %oldObj.disableMove; + resetPos = %oldObj.resetPos; + respawnTime = %oldObj.respawnTime; + marker = %oldObj; + }; + } + return(%obj); +} + +function WheeledVehicleData::create(%data, %team, %oldObj) +{ + if(%oldObj $= "") + { + %obj = new WheeledVehicle() + { + dataBlock = %data; + respawn = "0"; + teamBought = %team; + team = %team; + }; + } + else + { + %obj = new WheeledVehicle() + { + dataBlock = %data; + respawn = %oldObj.respawn; // z0dd - ZOD, 4/10/04. Fixed, was 0 + teamBought = %team; + team = %team; + mountable = %oldObj.mountable; + disableMove = %oldObj.disableMove; + resetPos = %oldObj.resetPos; + deployed = %oldObj.deployed; + respawnTime = %oldObj.respawnTime; + marker = %oldObj; + }; + } + + return(%obj); +} + +function FlyingVehicleData::create(%data, %team, %oldObj) +{ + if(%oldObj $= "") + { + %obj = new FlyingVehicle() + { + dataBlock = %data; + respawn = "0"; + teamBought = %team; + team = %team; + }; + } + else + { + %obj = new FlyingVehicle() + { + dataBlock = %data; + respawn = %oldObj.respawn; // z0dd - ZOD, 4/10/04. Fixed, was missing this param + teamBought = %team; + team = %team; + mountable = %oldObj.mountable; + disableMove = %oldObj.disableMove; + resetPos = %oldObj.resetPos; + respawnTime = %oldObj.respawnTime; + marker = %oldObj; + }; + } + + return(%obj); +} + +function FlyingVehicleData::switchSidesSetPos(%data, %oldObj) +{ + %team = %oldObj.curTeam == 1 ? 2 : 1; + %oldObj.curTeam = %team; + %obj = new FlyingVehicle() + { + dataBlock = %data; + respawn = %oldObj.respawn; // z0dd - ZOD, 4/10/04. Was missing this param + teamBought = %team; + team = %team; + mountable = %oldObj.mountable; + disableMove = %oldObj.disableMove; + resetPos = %oldObj.resetPos; + respawnTime = %oldObj.respawnTime; + marker = %oldObj; + }; + %obj.setTransform(%oldObj.getTransform()); + + return(%obj); +} + +function WheeledVehicleData::switchSidesSetPos(%data, %oldObj) +{ + %team = %oldObj.curTeam == 1 ? 2 : 1; + %oldObj.curTeam = %team; + %obj = new WheeledVehicle() + { + dataBlock = %data; + respawn = %oldObj.respawn; // z0dd - ZOD, 4/10/04. Lagg_Alot - Fixed, was 0 + teamBought = %team; + team = %team; + mountable = %oldObj.mountable; + disableMove = %oldObj.disableMove; + resetPos = %oldObj.resetPos; + deployed = %oldObj.deployed; + respawnTime = %oldObj.respawnTime; + marker = %oldObj; + }; + %obj.setTransform(%oldObj.getTransform()); + return(%obj); +} + +// z0dd - ZOD, 4/10/04. Lagg_Alot - Function was named incorrectly +//function HoverVehicleData::switchSides(%data, %oldObj) +function HoverVehicleData::switchSidesSetPos(%data, %oldObj) +{ + %team = %oldObj.curTeam == 1 ? 2 : 1; + %oldObj.curTeam = %team; + %obj = new HoverVehicle() + { + dataBlock = %data; + respawn = %oldObj.respawn; // z0dd - ZOD, 4/10/04. Lagg_Alot - Fixed, was 0 + teamBought = %team; + team = %team; + mountable = %oldObj.mountable; + disableMove = %oldObj.disableMove; + resetPos = %oldObj.resetPos; + respawnTime = %oldObj.respawnTime; + marker = %oldObj; + }; + %obj.setTransform(%oldObj.getTransform()); + return(%obj); +} + +function resetNonStaticObjPositions() +{ + MissionGroup.setupPositionMarkers(false); + MissionCleanup.positionReset(); +} + +function next(%team) +{ + ResetObjsPositions(%team); +} + +function SimGroup::positionReset(%group) +{ + for(%i = %group.getCount() - 1; %i >=0 ; %i--) + { + %obj = %group.getObject(%i); + if(%obj.resetPos && %obj.getName() !$= PosMarker) + %obj.delete(); + else + %obj.positionReset(); + } + + for(%i = 0; %i < %group.getCount(); %i++) + { + %obj = %group.getObject(%i); + if(%obj.getName() $= PosMarker) + { + cancel(%obj.schedule); + %newObj = %obj.data.switchSidesSetPos(%obj); + MissionCleanup.add(%newObj); + setTargetSensorGroup(%newObj.target, %newObj.team); + } + else + %obj.positionReset(); + } +} + +function VehicleData::respawn(%data, %marker) +{ + %mask = $TypeMasks::PlayerObjectType | $TypeMasks::VehicleObjectType | $TypeMasks::TurretObjectType; + InitContainerRadiusSearch(%marker.getWorldBoxCenter(), %data.checkRadius, %mask); + if(containerSearchNext() == 0) + { + %newObj = %data.create(%marker.curTeam, %marker); + %newObj.startFade(1000, 0, false); + %newObj.setTransform(%marker.getTransform()); + + setTargetSensorGroup(%newObj.target, %newObj.team); + MissionCleanup.add(%newObj); + } + else + { + %marker.schedule = %data.schedule(3000, "respawn", %marker); + } +} + +function SimObject::positionReset(%group, %team) +{ + //Used to avoid warnings +} + +function Terraformer::positionReset(%group, %team) +{ + //Used to avoid warnings +} + +function SimGroup::setupPositionMarkers(%group, %create) +{ + for(%i = %group.getCount() - 1; %i >= 0; %i--) + { + %obj = %group.getObject(%i); + if(%obj.resetPos || %obj.respawnTime !$= "") + { + if(%create) + { + %marker = %obj.getDataBlock().createPositionMarker(%obj); + MissionCleanup.add(%marker); + %obj.marker = %marker; + } + else + { + %obj.delete(); + } + } + else + %obj.setupPositionMarkers(%create); + } +} + +function SimObject::setupPositionMarkers(%group, %create) +{ + //Used to avoid warnings +} + +function VehicleData::createPositionMarker(%data, %obj) +{ + %marker = new Trigger(PosMarker) + { + dataBlock = markerTrigger; + mountable = %obj.mountable; + disableMove = %obj.disableMove; + resetPos = %obj.resetPos; + data = %obj.getDataBlock().getName(); + deployed = %obj.deployed; + curTeam = %obj.team; + respawnTime = %obj.respawnTime; + }; + %marker.setTransform(%obj.getTransform()); + return %marker; +} + +function VehicleData::hasDismountOverrides(%data, %obj) +{ + return false; +} + diff --git a/vehicles/vehicle.cs.bak b/vehicles/vehicle.cs.bak new file mode 100644 index 0000000..2eecbc4 --- /dev/null +++ b/vehicles/vehicle.cs.bak @@ -0,0 +1,1697 @@ +// Notes: +// - respawning vehicles with turrets (bomber/tank) will not setup the turret properly + +//Damage Rate for entering Liquid +$VehicleDamageLava = 0.0325; +$VehicleDamageHotLava = 0.0325; +$VehicleDamageCrustyLava = 0.0325; + +$NumVehiclesDeploy = 0; + +//************************************************************** +//* GENERAL PURPOSE FUNCTIONS +//************************************************************** + +function VehicleData::onAdd(%data, %obj) +{ + Parent::onAdd(%data, %obj); + if((%data.sensorData !$= "") && (%obj.getTarget() != -1)) + setTargetSensorData(%obj.getTarget(), %data.sensorData); + + %obj.setRechargeRate(%data.rechargeRate); + // set full energy + %obj.setEnergyLevel(%data.MaxEnergy); + + if(%obj.disableMove) + %obj.immobilized = true; + + if(%obj.deployed) + { + // z0dd - ZOD, 5/12/04. This is retarded. This causes many problems in Siege and + // Tournament mode for all game types. Deploy these vehicles regardless. + //if($countDownStarted) + // %data.schedule(($Host::WarmupTime * 1000) / 2, "vehicleDeploy", %obj, 0, 1); + //else + //{ + // $VehiclesDeploy[$NumVehiclesDeploy] = %obj; + // $NumVehiclesDeploy++; + //} + if($Host::WarmupTime >= 2) + %data.schedule(($Host::WarmupTime * 1000) / 2, "vehicleDeploy", %obj, 0, 1); + else + %data.schedule(2000, "vehicleDeploy", %obj, 0, 1); + } + if(%obj.mountable || %obj.mountable $= "") + %data.isMountable(%obj, true); + else + %data.isMountable(%obj, false); + + %obj.setSelfPowered(); +// %data.canObserve = true; +} + +function VehicleData::onRemove(%this, %obj) +{ + // if there are passengers/driver, kick them out + %this.deleteAllMounted(%obj); + for(%i = 0; %i < %obj.getDatablock().numMountPoints; %i++) + if (%obj.getMountNodeObject(%i)) { + %passenger = %obj.getMountNodeObject(%i); + %passenger.unmount(); + } + vehicleListRemove(%obj.getDataBlock(), %obj); + if(%obj.lastPilot.lastVehicle == %obj) + %obj.lastPilot.lastVehicle = ""; + + Parent::onRemove(%this, %obj); +} + +// z0dd - ZOD, 5/17/03. New function. Unlimited energy bug fix. +function MobileBaseVehicle::onRemove(%this, %obj) +{ + if(%obj.station.triggeredBy !$= "") + %obj.station.getDataBlock().onLeaveTrigger(%obj.station.trigger, %obj.station.triggeredBy); + + %obj.station.notDeployed = true; + Parent::onRemove(%this, %obj); +} + +// z0dd - ZOD, 4/10/04. ilys - checkSpeed function. Kill the driver and MPB if going too fast. +function MobileBaseVehicle::checkSpeed(%data, %obj) +{ + if(!isObject(%obj)) + return; + + if(isEventPending(%obj.speedCheck)) + cancel(%obj.speedCheck); + + if(VectorLen(%obj.getVelocity()) > 200) + { + if(%obj.getMountNodeObject(0)) %obj.getMountNodeObject(0).scriptKill(36); + %obj.setDamagelevel(4); + } + %obj.speedCheck = %data.schedule(5000, "checkSpeed", %obj); +} + +function VehicleData::onDamage(%this,%obj) +{ + %damage = %obj.getDamageLevel(); + if (%damage >= %this.destroyedLevel) + { + if(%obj.getDamageState() !$= "Destroyed") + { + if(%obj.respawnTime !$= "") + %obj.marker.schedule = %obj.marker.data.schedule(%obj.respawnTime, "respawn", %obj.marker); + %obj.setDamageState(Destroyed); + } + } + else + { + if(%obj.getDamageState() !$= "Enabled") + %obj.setDamageState(Enabled); + } +} + +function VehicleData::playerDismounted(%data, %obj, %player) +{ + if( %player.client.observeCount > 0 ) + resetObserveFollow( %player.client, true ); + + setTargetSensorGroup(%obj.getTarget(), %obj.team); + + // if there is a turret, set its team as well. + if( %obj.turretObject > 0 ) + setTargetSensorGroup(%obj.turretObject.getTarget(), %obj.team); +} + +function HoverVehicle::useCreateHeight() +{ + //this function is declared to prevent console error msg spam... +} + +function WheeledVehicle::useCreateHeight() +{ + //this function is declared to prevent console error msg spam... +} + +function AssaultVehicle::onDamage(%this, %obj) +{ + if(isObject(%obj.getMountNodeObject(10))) + (%obj.getMountNodeObject(10)).setDamagelevel(%obj.getDamageLevel()); + Parent::onDamage(%this, %obj); +} + +function BomberFlyer::onDamage(%this, %obj) +{ + if(isObject(%obj.getMountNodeObject(10))) + (%obj.getMountNodeObject(10)).setDamagelevel(%obj.getDamageLevel()); + Parent::onDamage(%this, %obj); +} + +function MobileBaseVehicle::onDamage(%this, %obj) +{ + if(isObject(%obj.getMountNodeObject(1))) + (%obj.getMountNodeObject(1)).setDamagelevel(%obj.getDamageLevel()); + Parent::onDamage(%this, %obj); +} + +function VehicleData::onEnterLiquid(%data, %obj, %coverage, %type) +{ + switch(%type) + { + case 0: + //Water + %obj.setHeat(0.0); + case 1: + //Ocean Water + %obj.setHeat(0.0); + case 2: + //River Water + %obj.setHeat(0.0); + case 3: + //Stagnant Water + %obj.setHeat(0.0); + case 4: + //Lava + %obj.liquidDamage(%data, $VehicleDamageLava, $DamageType::Lava); + case 5: + //Hot Lava + %obj.liquidDamage(%data, $VehicleDamageHotLava, $DamageType::Lava); + case 6: + //Crusty Lava + %obj.liquidDamage(%data, $VehicleDamageCrustyLava, $DamageType::Lava); + case 7: + //Quick Sand + } +} + +function FlyingVehicle::liquidDamage(%obj, %data, %damageAmount, %damageType) +{ + if(%obj.getDamageState() !$= "Destroyed") + { + %data.damageObject(%obj, 0, "0 0 0", %damageAmount, %damageType); + %obj.lDamageSchedule = %obj.schedule(50, "liquidDamage", %data, %damageAmount, %damageType); + passengerLiquidDamage(%obj, %damageAmount, %damageType); + } + else + %obj.lDamageSchedule = ""; +} + +function WheeledVehicle::liquidDamage(%obj, %data, %damageAmount, %damageType) +{ + if(%obj.getDamageState() !$= "Destroyed") + { + %data.damageObject(%obj, 0, "0 0 0", %damageAmount, %damageType); + %obj.lDamageSchedule = %obj.schedule(50, "liquidDamage", %data, %damageAmount, %damageType); + passengerLiquidDamage(%obj, %damageAmount, %damageType); + } + else + %obj.lDamageSchedule = ""; +} + +function HoverVehicle::liquidDamage(%obj, %data, %damageAmount, %damageType) +{ + if(%obj.getDamageState() !$= "Destroyed") + { + %data.damageObject(%obj, 0, "0 0 0", %damageAmount, %damageType); + %obj.lDamageSchedule = %obj.schedule(50, "liquidDamage", %data, %damageAmount, %damageType); + passengerLiquidDamage(%obj, %damageAmount, %damageType); + } + else + %obj.lDamageSchedule = ""; +} + +function passengerLiquidDamage(%obj, %damageAmount, %damageType) +{ + for(%i = %num; %i < %obj.getDataBlock().numMountPoints; %i++) + if (%p = %obj.getMountNodeObject(%i)) + %p.liquidDamage(%p.getDatablock(), $DamageLava, $DamageType::Lava); +} + +function VehicleData::onLeaveLiquid(%data, %obj, %type) +{ + switch(%type) + { + case 0: + //Water + %obj.setHeat(1.0); + case 1: + //Ocean Water + %obj.setHeat(1.0); + case 2: + //River Water + %obj.setHeat(1.0); + case 3: + //Stagnant Water + %obj.setHeat(1.0); + case 4: + //Lava + case 5: + //Hot Lava + case 6: + //Crusty Lava + case 7: + //Quick Sand + } + + if(%obj.lDamageSchedule !$= "") + { + cancel(%obj.lDamageSchedule); + %obj.lDamageSchedule = ""; + } +} + +function VehicleData::onDestroyed(%data, %obj, %prevState) +{ + if(%obj.lastDamagedBy) + { + %destroyer = %obj.lastDamagedBy; + game.vehicleDestroyed(%obj, %destroyer); + //error("vehicleDestroyed( "@ %obj @", "@ %destroyer @")"); + } + + radiusVehicleExplosion(%data, %obj); + if(%obj.turretObject) + if(%obj.turretObject.getControllingClient()) + %obj.turretObject.getDataBlock().playerDismount(%obj.turretObject); + for(%i = 0; %i < %obj.getDatablock().numMountPoints; %i++) + { + if (%obj.getMountNodeObject(%i)) { + %flingee = %obj.getMountNodeObject(%i); + %flingee.getDataBlock().doDismount(%flingee, true); + %xVel = 250.0 - (getRandom() * 500.0); + %yVel = 250.0 - (getRandom() * 500.0); + %zVel = (getRandom() * 100.0) + 50.0; + %flingVel = %xVel @ " " @ %yVel @ " " @ %zVel; + %flingee.applyImpulse(%flingee.getTransform(), %flingVel); + echo("got player..." @ %flingee.getClassName()); + %flingee.damage(0, %obj.getPosition(), 0.4, $DamageType::Crash); + } + } + + %data.deleteAllMounted(%obj); + // ----------------------------------------------------------------------------------------- + // z0dd - ZOD - Czar, 6/24/02. Move this vehicle out of the way so nothing collides with it. + if(%data.getName() $="BomberFlyer" || %data.getName() $="MobileBaseVehicle" || %data.getName() $="AssaultVehicle") + { + %obj.setFrozenState(true); + %obj.schedule(2000, "delete"); + %data.schedule(500, 'onAvoidCollisions', %obj); + } + else + { + %obj.setFrozenState(true); + %obj.schedule(500, "delete"); + } + // ----------------------------------------------------------------------------------------- +} + +// ---------------------------------------------------------------------------------- +// z0dd - ZOD, 6/13/02. Move this vehicle out of the way so nothing collides with it. +function VehicleData::onAvoidCollisions(%data, %obj) +{ + // Get the current location of the vehicle and send it to the moon! + %transform = posFromTransform(%obj.getTransform()); + %position = getWord(%transform, 0) SPC getWord(%transform, 1) SPC (getWord(%transform, 2)+2000); + %rotation = getWord(%transform, 3) SPC getWord(%transform, 4) SPC getWord(%transform, 5) SPC getWord(%transform, 6); + %obj.setTransform(%position SPC %rotation); +} +// ---------------------------------------------------------------------------------- + +// z0dd - ZOD, 5/12/04. Damage vehicles that collide with armor +function VehicleData::onCollision( %data, %obj, %col ) +{ + // Keep vehicle ghost from harming players? + if(%obj.getDamageState() $= "Destroyed") + return; + + if(!isObject(%obj) || !isObject(%col)) + return; + + if($Host::ClassicLoadVRamChanges) + { + if(%col.getDataBlock().className $= "Armor") + { + %vel = %obj.getVelocity(); + %vecLen = vectorDot(%vel, vectorNormalize(%vel)); + if(%vecLen > %data.collDamageThresholdVel) + { + %data.damageObject(%obj, %col, VectorAdd(%vel, %obj.getPosition()), %vecLen * %data.collDamageMultiplier, $DamageType::Impact); + } + %obj.playAudio(0, %data.hardImpactSound); + } + } +} + +function radiusVehicleExplosion(%data, %vehicle) +{ + // this is a modified version of RadiusExplosion() from projectiles.cs + %position = %vehicle.getPosition(); + InitContainerRadiusSearch(%position, %data.explosionRadius, $TypeMasks::PlayerObjectType | + $TypeMasks::VehicleObjectType | + $TypeMasks::MoveableObjectType | + $TypeMasks::StaticShapeObjectType | + $TypeMasks::ForceFieldObjectType | + $TypeMasks::TurretObjectType | + $TypeMasks::ItemObjectType); + + %numTargets = 0; + while ((%targetObject = containerSearchNext()) != 0) + { + if(%targetObject == %vehicle) + continue; + + %dist = containerSearchCurrRadDamageDist(); + + if (%dist > %data.explosionRadius) + continue; + + if (%targetObject.isMounted()) + { + %mount = %targetObject.getObjectMount(); + if(%mount == %vehicle) + continue; + + %found = -1; + for (%i = 0; %i < %mount.getDataBlock().numMountPoints; %i++) + { + if (%mount.getMountNodeObject(%i) == %targetObject) + { + %found = %i; + break; + } + } + + if (%found != -1) + { + if (%mount.getDataBlock().isProtectedMountPoint[%found] && (%mount != %vehicle)) + continue; + } + } + + %targets[%numTargets] = %targetObject; + %targetDists[%numTargets] = %dist; + %numTargets++; + } + + for (%i = 0; %i < %numTargets; %i++) + { + %targetObject = %targets[%i]; + %dist = %targetDists[%i]; + + %coverage = calcExplosionCoverage(%position, %targetObject, + ($TypeMasks::InteriorObjectType | + $TypeMasks::TerrainObjectType | + $TypeMasks::ForceFieldObjectType)); + if (%coverage == 0) + continue; + + %amount = (1.0 - (%dist / %data.explosionRadius)) * %coverage * %data.explosionDamage; + %targetData = %targetObject.getDataBlock(); + + %momVec = "0 0 1"; + + if(%amount > 0) + %targetData.damageObject(%targetObject, %sourceObject, %position, %amount, $DamageType::Explosion, %momVec); + } +} + +function VehicleData::deleteAllMounted() +{ + +} + +//************************************************************** +//* VEHICLE CREATION +//************************************************************** +// (NOTE: No entry for Wildcat Grav Cycle is here. -- DG) + +//---------------------------- +// SHRIKE SCOUT FLIER +//---------------------------- + +function ScoutFlyer::onAdd(%this, %obj) +{ + Parent::onAdd(%this, %obj); + %obj.mountImage(ScoutChaingunParam, 0); + %obj.mountImage(ScoutChaingunImage, 2); + %obj.mountImage(ScoutChaingunPairImage, 3); + %obj.nextWeaponFire = 2; + %obj.schedule(5500, "playThread", $ActivateThread, "activate"); +} + +//---------------------------- +// THUNDERSWORD BOMBER +//---------------------------- + +function BomberFlyer::onAdd(%this, %obj) +{ + Parent::onAdd(%this, %obj); + + %turret = TurretData::create(BomberTurret); + MissionCleanup.add(%turret); + %turret.team = %obj.teamBought; + %turret.selectedWeapon = 1; + %turret.setSelfPowered(); + %obj.mountObject(%turret, 10); + %turret.mountImage(BomberTurretBarrel,2); + %turret.mountImage(BomberTurretBarrelPair,3); + %turret.mountImage(BomberBombImage, 4); + %turret.mountImage(BomberBombPairImage, 5); + %turret.mountImage(BomberTargetingImage, 6); + %obj.turretObject = %turret; + %turret.setCapacitorRechargeRate( %turret.getDataBlock().capacitorRechargeRate ); + %turret.vehicleMounted = %obj; + + //vehicle turrets should not auto fire at targets + %turret.setAutoFire(false); + + //for this particular weapon - a non-firing datablock used only so the AI can aim the turret + //Also needed so we can set the turret parameters.. + %turret.mountImage(AIAimingTurretBarrel, 0); + + // setup the turret's target info + setTargetSensorGroup(%turret.getTarget(), %turret.team); + setTargetNeverVisMask(%turret.getTarget(), 0xffffffff); +} + +//---------------------------- +// HAVOC TRANSPORT FLIER +//---------------------------- + +function HAPCFlyer::onAdd(%this, %obj) +{ + Parent::onAdd(%this, %obj); + %obj.schedule(6000, "playThread", $ActivateThread, "activate"); +} + +//---------------------------- +// BEOWULF ASSAULT VEHICLE +//---------------------------- + +function AssaultVehicle::onAdd(%this, %obj) +{ + Parent::onAdd(%this, %obj); + + %turret = TurretData::create(AssaultPlasmaTurret); + %turret.selectedWeapon = 1; + MissionCleanup.add(%turret); + %turret.team = %obj.teamBought; + %turret.setSelfPowered(); + %obj.mountObject(%turret, 10); + %turret.mountImage(AssaultPlasmaTurretBarrel, 2); + %turret.mountImage(AssaultMortarTurretBarrel, 4); + %turret.setCapacitorRechargeRate( %turret.getDataBlock().capacitorRechargeRate ); + %obj.turretObject = %turret; + + //vehicle turrets should not auto fire at targets + %turret.setAutoFire(false); + + //Needed so we can set the turret parameters.. + %turret.mountImage(AssaultTurretParam, 0); + %obj.schedule(6000, "playThread", $ActivateThread, "activate"); + + // set the turret's target info + setTargetSensorGroup(%turret.getTarget(), %turret.team); + setTargetNeverVisMask(%turret.getTarget(), 0xffffffff); +} + +//---------------------------- +// JERICHO FORWARD BASE (Mobile Point Base) +//---------------------------- + +function MobileBaseVehicle::onAdd(%this, %obj) +{ + Parent::onAdd(%this, %obj); + %obj.station = ""; + %obj.turret = ""; + %obj.beacon = ""; + + %obj.schedule(5000, "playThread", $AmbientThread, "ambient"); + + // z0dd - ZOD, 4/10/04. ilys - Start checkSpeed schedule on MPB + %this.checkSpeed(%obj); +} + +//************************************************************** +//* MULTI-CREW VEHICLE DELETION +//************************************************************** + +//---------------------------- +//BEOWULF ASSAULT VEHICLE +//---------------------------- + +function AssaultVehicle::deleteAllMounted(%data, %obj) +{ + %turret = %obj.getMountNodeObject(10); + if(!isObject(%turret)) + return; + + if(%client = %turret.getControllingClient()) + { + %client.player.setControlObject(%client.player); + %client.player.mountImage(%client.player.lastWeapon, $WeaponSlot); + %client.player.mountVehicle = false; + } + %turret.schedule(2000, "delete"); +} + +//---------------------------- +// THUNDERSWORD BOMBER +//---------------------------- + +function BomberFlyer::deleteAllMounted(%data, %obj) +{ + if(isObject(%obj.beacon)) + %obj.beacon.schedule(50, delete); + + %turret = %obj.getMountNodeObject(10); + if(!%turret) + return; + + %turret.altTrigger = 0; + %turret.fireTrigger = 0; + + if(%client = %turret.getControllingClient()) + { + commandToClient(%client, 'endBomberSight'); + %client.player.setControlObject(%client.player); + %client.player.mountImage(%client.player.lastWeapon, $WeaponSlot); + %client.player.mountVehicle = false; + + %client.player.bomber = false; + %client.player.isBomber = false; + } + %turret.schedule(2000, delete); +} + +//---------------------------- +// JERICHO FORWARD BASE +//---------------------------- + +function MobileBaseVehicle::deleteAllMounted(%data, %obj) +{ + if(isEventPending(%obj.speedCheck)) + cancel(%obj.speedCheck); + + if(isObject(%obj.station)) // z0dd - ZOD, 4/25/02. Console spam fix. + { + if(%obj.station.triggeredBy !$= "") + %obj.station.getDataBlock().onLeaveTrigger(%obj.station.trigger, %obj.station.triggeredBy); + + %obj.station.notDeployed = true; + %obj.station.getDataBlock().onLosePowerDisabled(%obj.station); + %obj.unmountObject(%obj.station); + %obj.station.trigger.schedule(1000, delete); + %obj.station.schedule(2000, delete); + } + if(isObject(%obj.turret)) // z0dd - ZOD, 4/25/02. Console spam fix. + { + %obj.turret.getDataBlock().onLosePowerDisabled(%obj.turret); + %obj.unmountObject(%obj.turret); + %obj.turret.schedule(2000, delete); + } + //-------------------------------------------------------------------------- + // z0dd - ZOD, 4/25/02. MPB Teleporter. +// if (isObject(%obj.teleporter)) +// { +// %obj.teleporter.setThreadDir($ActivateThread, FALSE); +// %obj.teleporter.playThread($ActivateThread,"activate"); +// %obj.teleporter.playAudio($ActivateSound, StationVehicleDeactivateSound); +// } + //-------------------------------------------------------------------------- + if(isObject(%obj.shield)) + %obj.shield.schedule(2000, delete); + + if(isObject(%obj.beacon)) + { + %obj.beacon.schedule(0, delete); + } +} + +//************************************************************** +//* WEAPON MOUNTING ON VEHICLES +//************************************************************** + +//---------------------------- +// SHRIKE SCOUT FLIER +//---------------------------- + +function ScoutFlyer::playerMounted(%data, %obj, %player, %node) +{ + // scout flyer == SUV (single-user vehicle) + commandToClient(%player.client, 'setHudMode', 'Pilot', "Shrike", %node); + $numVWeapons = 1; + + // update observers who are following this guy... + if( %player.client.observeCount > 0 ) + resetObserveFollow( %player.client, false ); +} + +//---------------------------- +// THUNDERSWORD BOMBER +//---------------------------- + +function BomberFlyer::playerMounted(%data, %obj, %player, %node) +{ + if(%node == 0) + { + // pilot position + %player.setPilot(true); + commandToClient(%player.client, 'setHudMode', 'Pilot', "Bomber", %node); + } + else if(%node == 1) + { + // bombardier position + %turret = %obj.getMountNodeObject(10); + %player.vehicleTurret = %turret; + %player.setTransform("0 0 0 0 0 1 0"); + %player.lastWeapon = %player.getMountedImage($WeaponSlot); + %player.unmountImage($WeaponSlot); + if(!%player.client.isAIControlled()) + { + %player.setControlObject(%turret); + %player.client.setObjectActiveImage(%turret, 2); + } + commandToClient(%player.client, 'startBomberSight'); + %turret.bomber = %player; + $bWeaponActive = 0; + %obj.getMountNodeObject(10).selectedWeapon = 1; + commandToClient(%player.client,'SetWeaponryVehicleKeys', true); + + commandToClient(%player.client, 'setHudMode', 'Pilot', "Bomber", %node); + %player.isBomber = true; + } + else + { + // tail gunner position + commandToClient(%player.client, 'setHudMode', 'Passenger', "Bomber", %node); + } + // build a space-separated string representing passengers + // 0 = no passenger; 1 = passenger (e.g. "1 0 0 ") + %passString = buildPassengerString(%obj); + // send the string of passengers to all mounted players + for(%i = 0; %i < %data.numMountPoints; %i++) + if(%obj.getMountNodeObject(%i) > 0) + commandToClient(%obj.getMountNodeObject(%i).client, 'checkPassengers', %passString); + + // update observers who are following this guy... + if( %player.client.observeCount > 0 ) + resetObserveFollow( %player.client, false ); +} + +//---------------------------- +// HAVOC TRANSPORT FLIER +//---------------------------- + +function HAPCFlyer::playerMounted(%data, %obj, %player, %node) +{ + if(%node == 0) { + // pilot position + commandToClient(%player.client, 'setHudMode', 'Pilot', "HAPC", %node); + } + else { + // all others + commandToClient(%player.client, 'setHudMode', 'Passenger', "HAPC", %node); + } + // build a space-separated string representing passengers + // 0 = no passenger; 1 = passenger (e.g. "1 0 0 1 1 0 ") + %passString = buildPassengerString(%obj); + // send the string of passengers to all mounted players + for(%i = 0; %i < %data.numMountPoints; %i++) + if(%obj.getMountNodeObject(%i) > 0) + commandToClient(%obj.getMountNodeObject(%i).client, 'checkPassengers', %passString); + + // update observers who are following this guy... + if( %player.client.observeCount > 0 ) + resetObserveFollow( %player.client, false ); +} + +//---------------------------- +// WILDCAT GRAV CYCLE +//---------------------------- + +function ScoutVehicle::playerMounted(%data, %obj, %player, %node) +{ + // scout vehicle == SUV (single-user vehicle) + commandToClient(%player.client, 'setHudMode', 'Pilot', "Hoverbike", %node); + + // update observers who are following this guy... + if( %player.client.observeCount > 0 ) + resetObserveFollow( %player.client, false ); +} + +//---------------------------- +// BEOWULF ASSAULT VEHICLE +//---------------------------- + +function AssaultVehicle::playerMounted(%data, %obj, %player, %node) +{ + if(%node == 0) { + // driver position + // is there someone manning the turret? + //%turreteer = %obj.getMountedNodeObject(1); + commandToClient(%player.client, 'setHudMode', 'Pilot', "Assault", %node); + } + else if(%node == 1) + { + // turreteer position + %turret = %obj.getMountNodeObject(10); + %player.vehicleTurret = %turret; + %player.setTransform("0 0 0 0 0 1 0"); + %player.lastWeapon = %player.getMountedImage($WeaponSlot); + %player.unmountImage($WeaponSlot); + if(!%player.client.isAIControlled()) + { + %player.setControlObject(%turret); + %player.client.setObjectActiveImage(%turret, 2); + } + %turret.turreteer = %player; + // if the player is the turreteer, show vehicle's weapon icons + //commandToClient(%player.client, 'showVehicleWeapons', %data.getName()); + //%player.client.setVWeaponsHudActive(1); // plasma turret icon (default) + + $aWeaponActive = 0; + commandToClient(%player.client,'SetWeaponryVehicleKeys', true); + %obj.getMountNodeObject(10).selectedWeapon = 1; + commandToClient(%player.client, 'setHudMode', 'Pilot', "Assault", %node); + } + + // update observers who are following this guy... + if( %player.client.observeCount > 0 ) + resetObserveFollow( %player.client, false ); + + // build a space-separated string representing passengers + // 0 = no passenger; 1 = passenger (e.g. "1 0 ") + %passString = buildPassengerString(%obj); + // send the string of passengers to all mounted players + for(%i = 0; %i < %data.numMountPoints; %i++) + if(%obj.getMountNodeObject(%i) > 0) + commandToClient(%obj.getMountNodeObject(%i).client, 'checkPassengers', %passString); +} + +//---------------------------- +// JERICHO FORWARD BASE +//---------------------------- + +function MobileBaseVehicle::playerMounted(%data, %obj, %player, %node) +{ + // MPB vehicle == SUV (single-user vehicle) + commandToClient(%player.client, 'setHudMode', 'Pilot', "MPB", %node); + if(%obj.deploySchedule) + { + %obj.deploySchedule.clear(); + %obj.deploySchedule = ""; + } + + if(%obj.deployed !$= "" && %obj.deployed == 1) + { + %obj.setThreadDir($DeployThread, false); + %obj.playThread($DeployThread,"deploy"); + %obj.playAudio($DeploySound, MobileBaseUndeploySound); + %obj.station.setThreadDir($DeployThread, false); + %obj.station.getDataBlock().onLosePowerDisabled(%obj.station); + %obj.station.clearSelfPowered(); + %obj.station.goingOut=false; + %obj.station.notDeployed = 1; + %obj.station.playAudio($DeploySound, MobileBaseStationUndeploySound); + + if((%turretClient = %obj.turret.getControllingClient()) !$= "") + { + CommandToServer( 'resetControlObject', %turretClient ); + } + + %obj.turret.setThreadDir($DeployThread, false); + %obj.turret.clearTarget(); + %obj.turret.setTargetObject(-1); + + %obj.turret.playAudio($DeploySound, MobileBaseTurretUndeploySound); + %obj.shield.open(); + %obj.shield.schedule(1000,"delete"); + %obj.deploySchedule = ""; + //----------------------------------------------------------------------- + // z0dd - ZOD, 4/25/02. MPB Teleporter. +// %obj.teleporter.setThreadDir($ActivateThread, FALSE); +// %obj.teleporter.playThread($ActivateThread,"activate"); +// %obj.teleporter.playAudio($ActivateSound, StationVehicleDeactivateSound); + //----------------------------------------------------------------------- + %obj.fullyDeployed = 0; + + %obj.noEnemyControl = 0; + } + %obj.deployed = 0; + + // update observers who are following this guy... + if( %player.client.observeCount > 0 ) + resetObserveFollow( %player.client, false ); +} + +function buildPassengerString(%vehicle) +{ + %passStr = ""; + for(%i = 0; %i < %vehicle.getDatablock().numMountPoints; %i++) + { + if(%vehicle.getMountNodeObject(%i) > 0) + %passStr = %passStr @ "1 "; + else + %passStr = %passStr @ "0 "; + } + + return %passStr; +} + +function MobileBaseVehicle::playerDismounted(%data, %obj, %player) +{ + %obj.schedule(500, "deployVehicle", %data, %player); + Parent::playerDismounted( %data, %obj, %player ); +} + +function WheeledVehicle::deployVehicle(%obj, %data, %player) +{ + if (!%data.vehicleDeploy(%obj, %player)) + %obj.schedule(500, "deployVehicle", %data, %player); +} + +//************************************************************** +//* JERICHO DEPLOYMENT and UNDEPLOYMENT +//************************************************************** + +function MobileBaseVehicle::vehicleDeploy(%data, %obj, %player, %force) +{ + if(VectorLen(%obj.getVelocity()) <= 0.1 || %force) + { + %deployMessage = ""; + if( (%deployMessage = %data.checkTurretDistance(%obj)) $= "" || %force) + { + if(%obj.station $= "") + { + if( (%deployMessage = %data.checkDeploy(%obj)) $= "" || %force) + { + %obj.station = new StaticShape() { + scale = "1 1 1"; + dataBlock = "MobileInvStation"; + lockCount = "0"; + homingCount = "0"; + team = %obj.team; + vehicle = %obj; + }; + %obj.station.startFade(0,0,true); + %obj.mountObject(%obj.station, 2); + %obj.station.getDataBlock().createTrigger(%obj.station); + %obj.station.setSelfPowered(); + %obj.station.playThread($PowerThread,"Power"); + %obj.station.playAudio($HumSound,StationHumSound); + %obj.station.vehicle = %obj; + %obj.turret = new turret() { + scale = "1 1 1"; + dataBlock = "MobileTurretBase"; + lockCount = "0"; + homingCount = "0"; + team = %obj.team; + }; + %obj.turret.setDamageLevel(%obj.getDamageLevel()); + %obj.mountObject(%obj.turret, 1); + %obj.turret.setSelfPowered(); + %obj.turret.playThread($PowerThread,"Power"); + + //%obj.turret.mountImage(MissileBarrelLarge, 0 ,false); + // ----------------------------------------------------- + // z0dd - ZOD, 4/25/02. modular MPB turret + if(%obj.barrel !$= "") + { + %obj.turret.mountImage(%obj.barrel, 0 ,false); + } + else + { + %obj.turret.mountImage(MissileBarrelLarge, 0 ,false); + } + // ----------------------------------------------------- + + %obj.beacon = new BeaconObject() { + dataBlock = "DeployedBeacon"; + position = %obj.position; + rotation = %obj.rotation; + team = %obj.team; + }; + %obj.beacon.setBeaconType(friend); + %obj.beacon.setTarget(%obj.team); + + checkSpawnPos(%obj, 20); + } + } + else + { + %obj.station.setSelfPowered(); + %obj.station.playThread($PowerThread,"Power"); + %obj.turret.setSelfPowered(); + %obj.turret.playThread($PowerThread,"Power"); + } + if(%deployMessage $= "" || %force) + { + if(%obj.turret.getTarget() == -1) + { + %obj.turret.setTarget(%obj.turret.target); + } + %obj.turret.setThreadDir($DeployThread, true); + %obj.turret.playThread($DeployThread,"deploy"); + %obj.turret.playAudio($DeploySound, MobileBaseTurretDeploySound); + + %obj.station.notDeployed = 1; + %obj.setThreadDir($DeployThread, true); + %obj.playThread($DeployThread,"deploy"); + %obj.playAudio($DeploySound, MobileBaseDeploySound); + %obj.deployed = 1; + %obj.deploySchedule = ""; + %obj.disableMove = true; + %obj.setFrozenState(true); + if(isObject(%obj.shield)) + %obj.shield.delete(); + + %obj.shield = new forceFieldBare() + { + scale = "1.22 1.8 1.1"; + dataBlock = "defaultTeamSlowFieldBare"; + team = %obj.team; + }; + %obj.shield.open(); + setTargetSensorData(%obj.getTarget(), MPBDeployedSensor); + } + } + if(%deployMessage !$= "") + messageClient(%player.client, '', %deployMessage); + + return true; + } + else + { + return false; + } +} + +function MobileBaseVehicle::onEndSequence(%data, %obj, %thread) +{ + if(%thread == $DeployThread && !%obj.deployed) + { + %obj.unmountObject(%obj.station); + %obj.station.trigger.delete(); + %obj.station.delete(); + %obj.station = ""; + + %obj.beacon.delete(); + + %obj.unmountObject(%obj.turret); + %obj.turret.delete(); + %obj.turret = ""; + + if(!%obj.immobilized) + { + %obj.disableMove = false; + %obj.setFrozenState(false); + } + setTargetSensorData(%obj.getTarget(), %data.sensorData); + } + else + { + %obj.station.startFade(0,0,false); + %obj.station.setThreadDir($DeployThread, true); + %obj.station.playThread($DeployThread,"deploy"); + %obj.station.playAudio($DeploySound, MobileBaseStationDeploySound); + %obj.station.goingOut = true; + %obj.shield.setTransform(%obj.getSlotTransform(3)); + %obj.shield.close(); + %obj.isDeployed = true; + %obj.noEnemyControl = 1; + } + + Parent::onEndSequence(%data, %obj, %thread); +} + +function MobileInvStation::onEndSequence(%data, %obj, %thread) +{ + if(!%obj.goingOut) + %obj.startFade(0,0,true); + else + { + %obj.notDeployed = 0; + %obj.vehicle.fullyDeployed = 1; + //-------------------------------------------------------------------------------- + // z0dd - ZOD, 4/25/02. MPB Teleporter. +// if(isObject(%obj.vehicle.teleporter)) +// { +// %obj.vehicle.teleporter.setThreadDir($ActivateThread, TRUE); +// %obj.vehicle.teleporter.playThread($ActivateThread,"activate"); +// %obj.vehicle.teleporter.playAudio($ActivateSound, StationVehicleAcitvateSound); +// } + //-------------------------------------------------------------------------------- + } + Parent::onEndSequence(%data, %obj, %thread); +} + +function MobileBaseVehicle::checkDeploy(%data, %obj) +{ + %mask = $TypeMasks::VehicleObjectType | $TypeMasks::MoveableObjectType | + $TypeMasks::StaticShapeObjectType | $TypeMasks::ForceFieldObjectType | + $TypeMasks::ItemObjectType | $TypeMasks::PlayerObjectType | + $TypeMasks::TurretObjectType | //$TypeMasks::StaticTSObjectType | + $TypeMasks::InteriorObjectType; + + //%slot 1 = turret %slot 2 = station + %height[1] = 0; + %height[2] = 0; + %radius[1] = 2.4; + %radius[2] = 2.4; + %stationFailed = false; + %turretFailed = false; + + for(%x = 1; %x < 3; %x++) + { + %posXY = getWords(%obj.getSlotTransform(%x), 0, 1); + %posZ = (getWord(%obj.getSlotTransform(%x), 2) + %height[%x]); + InitContainerRadiusSearch(%posXY @ " " @ %posZ, %radius[%x], %mask); + + while ((%objFound = ContainerSearchNext()) != 0) + { + if(%objFound != %obj) + { + if(%x == 1) + %turretFailed = true; + else + %stationFailed = true; + break; + } + } + } + + //If turret, station or both fail the send back the error message... + if(%turretFailed && %stationFailed) + return "Both Turret and Station are blocked and unable to deploy."; + if(%turretFailed) + return "Turret is blocked and unable to deploy."; + if(%stationFailed) + return "Station is blocked and unable to deploy."; + + //Check the station for collision with the Terrain + %mat = %obj.getTransform(); + for(%x = 1; %x < 7; %x+=2) + { + %startPos = MatrixMulPoint(%mat, %data.stationPoints[%x]); + %endPos = MatrixMulPoint(%mat, %data.stationPoints[%x+1]); + + %rayCastObj = containerRayCast(%startPos, %endPos, $TypeMasks::TerrainObjectType, 0); + if(%rayCastObj) + return "Station is blocked by terrain and unable to deploy."; + } + + return ""; +} +function MobileBaseVehicle::checkTurretDistance(%data, %obj) +{ + %pos = getWords(%obj.getTransform(), 0, 2); + InitContainerRadiusSearch(%pos, 20, $TypeMasks::TurretObjectType | $TypeMasks::InteriorObjectType); // z0dd - ZOD, 6/21/02. Allow closer deploy. Was 100 + while ((%objFound = ContainerSearchNext()) != 0) + { + if(%objFound.getType() & $TypeMasks::TurretObjectType) + { + if(%objFound.getDataBlock().ClassName $= "TurretBase") + return "Turret Base is in the area. Unable to deploy."; + } + else + { + %subStr = getSubStr(%objFound.interiorFile, 1, 4); + if(%subStr !$= "rock" && %subStr !$= "spir" && %subStr !$= "misc") + return "Building is in the area. Unable to deploy."; + } + } + return ""; +} + + +//************************************************************** +//* VEHICLE INVENTORY MANAGEMENT +//************************************************************** + +//-------------------------------------------------------------- +// NUMBER OF PURCHASEABLE VEHICLES PER TEAM +//-------------------------------------------------------------- + +$VehicleRespawnTime = 15000; +$Vehiclemax[ScoutVehicle] = 4; +$VehicleMax[AssaultVehicle] = 3; +$VehicleMax[MobileBaseVehicle] = 1; +$VehicleMax[ScoutFlyer] = 4; +$VehicleMax[BomberFlyer] = 2; +$VehicleMax[HAPCFlyer] = 2; + +function vehicleListRemove(%data, %obj) +{ + %blockName = %data.getName(); + for($i = 0; %i < $VehicleMax[%blockName]; %i++) + if($VehicleInField[%obj.team, %blockName, %i] == %obj) + { + $VehicleInField[%obj.team, %blockName, %i] = 0; + $VehicleTotalCount[%obj.team, %blockName]--; + break; + } +} + +function vehicleListAdd(%blockName, %obj) +{ + for($i = 0; %i < $VehicleMax[%blockName]; %i++) + { + if($VehicleInField[%obj.team, %blockName, %i] $= "" || $VehicleInField[%obj.team, %blockName, %i] == 0) + { + $VehicleInField[%obj.team, %blockName, %i] = %obj; + $VehicleTotalCount[%obj.team, %blockName]++; + break; + } + } +} + +function clearVehicleCount(%team) +{ + $VehicleTotalCount[%team, ScoutVehicle] = 0; + $VehicleTotalCount[%team, AssaultVehicle] = 0; + $VehicleTotalCount[%team, MobileBaseVehicle] = 0; + $VehicleTotalCount[%team, ScoutFlyer] = 0; + $VehicleTotalCount[%team, BomberFlyer] = 0; + $VehicleTotalCount[%team, HAPCFlyer] = 0; +} + +//************************************************************** +//* VEHICLE HUD SEAT INDICATOR LIGHTS +//************************************************************** + +// --------------------------------------------------------- +// z0dd - ZOD, 6/18/02. Get the name of the vehicle node and +// pass to Armor::onMount and Armor::onDismount in player.cs +function findNodeName(%vehicle, %node) +{ + %vName = %vehicle.getDataBlock().getName(); + if(%vName !$= "HAPCFlyer") + { + if(%node == 0) + return 'pilot'; + else if(%node == 1) + return 'gunner'; + else + return 'tailgunner'; + } + else + { + if(%node == 0) + return 'pilot'; + else if(%node == 1) + return 'tailgunner'; + else + return 'passenger'; + } +} + +// z0dd - ZOD, 4/10/04. Lagg_Alot, re-write. +function findAIEmptySeat(%vehicle, %player) +{ + %myArmor = %player.getArmorSize(); + %dataBlock = %vehicle.getDataBlock(); + if (%dataBlock.getName() $= "BomberFlyer" && %myArmor $= "Heavy") + %num = 2; + else + %num = 1; + + %node = -1; + for(%i = %num; %i < %dataBlock.numMountPoints; %i++) + { + if (!%vehicle.getMountNodeObject(%i)) + { + //cheap hack - for now, AI's will mount the next available node regardless of where they collided + %node = %i; + break; + } + } + //return the empty seat + return %node; +} + +function findEmptySeat(%vehicle, %player, %forceNode) +{ + %minNode = 1; + %node = -1; + %dataBlock = %vehicle.getDataBlock(); + %dis = %dataBlock.minMountDist; + %playerPos = getWords(%player.getTransform(), 0, 2); + %message = ""; + if(%dataBlock.lightOnly) + { + if(%player.client.armor $= "Light") + %minNode = 0; + else + %message = '\c2Only Scout Armors can pilot this vehicle.~wfx/misc/misc.error.wav'; + } + else if(%player.client.armor $= "Light" || %player.client.armor $= "Medium") + %minNode = 0; + else + %minNode = findFirstHeavyNode(%dataBlock); + + if(%forceNode !$= "") + %node = %forceNode; + else + { + for(%i = 0; %i < %dataBlock.numMountPoints; %i++) + if(!%vehicle.getMountNodeObject(%i)) + { + %seatPos = getWords(%vehicle.getSlotTransform(%i), 0, 2); + %disTemp = VectorLen(VectorSub(%seatPos, %playerPos)); + if(%disTemp <= %dis) + { + %node = %i; + %dis = %disTemp; + } + } + } + if(%node != -1 && %node < %minNode) + { + if(%message $= "") + { + if(%node == 0) + %message = '\c2Only Scout or Assault Armors can pilot this vehicle.~wfx/misc/misc.error.wav'; + else + %message = '\c2Only Scout or Assault Armors can use that position.~wfx/misc/misc.error.wav'; + } + + if(!%player.noSitMessage) + { + %player.noSitMessage = true; + %player.schedule(2000, "resetSitMessage"); + messageClient(%player.client, 'MsgArmorCantMountVehicle', %message); + } + %node = -1; + } + return %node; +} + +function findFirstHeavyNode(%data) +{ + for(%i = 0; %i < %data.numMountPoints; %i++) + if(%data.mountPose[%i] $= "") + return %i; + return %data.numMountPoints; +} + +//************************************************************** +//* DAMAGE FUNCTIONS +//************************************************************** + +function VehicleData::damageObject(%data, %targetObject, %sourceObject, %position, %amount, %damageType, %momVec, %theClient, %proj) +{ + if(%proj !$= "") + { + if(%amount > 0 && %targetObject.lastDamageProj !$= %proj) + { + %targetObject.lastDamageProj = %proj; + %targetObject.lastDamageAmount = %amount; + } + else if(%targetObject.lastDamageAmount < %amount) + %amount = %amount - %targetObject.lastDamageAmount; + else + return; + } + + // check for team damage + %sourceClient = %sourceObject ? %sourceObject.getOwnerClient() : 0; + %targetTeam = getTargetSensorGroup(%targetObject.getTarget()); + + if(%sourceClient) + %sourceTeam = %sourceClient.getSensorGroup(); + else if(isObject(%sourceObject) && %sourceObject.getClassName() $= "Turret") + { + %sourceTeam = getTargetSensorGroup(%sourceObject.getTarget()); + %sourceClient = %sourceObject.getControllingClient(); // z0dd - ZOD, 6/10/02. Play a sound to client when they hit a vehicle with a controlled turret + } + else + { + %sourceTeam = %sourceObject ? getTargetSensorGroup(%sourceObject.getTarget()) : -1; + // Client is allready defined and this spams console - ZOD + //%sourceClient = %sourceObject.getControllingClient(); // z0dd - ZOD, 6/10/02. Play a sound to client when they hit a vehicle from a vehicle + } + + // vehicles no longer obey team damage -JR +// if(!$teamDamage && (%targetTeam == %sourceTeam) && %targetObject.getDamagePercent() > 0.5) +// return; + //but we do want to track the destroyer + if(%sourceObject) + { + %targetObject.lastDamagedBy = %sourceObject; + %targetObject.lastDamageType = %damageType; + } + else + %targetObject.lastDamagedBy = 0; + + // ---------------------------------------------------------------------------------- + // z0dd - ZOD, 6/10/02. Play a sound to client when they hit a vehicle + if(%sourceClient && %sourceClient.vehicleHitSound) + { + if(%targetTeam != %sourceTeam) + { + if ((%damageType > 0 && %damageType < 11) || + (%damageType == 13) || + (%damageType > 15 && %damageType < 24) || + (%damageType > 25 && %damageType < 32)) + { + messageClient(%sourceClient, 'MsgClientHit', %sourceClient.vehicleHitWav); + } + } + } + // ---------------------------------------------------------------------------------- + + // Scale damage type & include shield calculations... + if (%data.isShielded) + %amount = %data.checkShields(%targetObject, %position, %amount, %damageType); + + + %damageScale = %data.damageScale[%damageType]; + if(%damageScale !$= "") + %amount *= %damageScale; + + if(%amount != 0) + %targetObject.applyDamage(%amount); + + if(%targetObject.getDamageState() $= "Destroyed" ) + { + if( %momVec !$= "") + %targetObject.setMomentumVector(%momVec); + } +} + +function VehicleData::onImpact(%data, %vehicleObject, %collidedObject, %vec, %vecLen) +{ + if(%vecLen > %data.minImpactSpeed) + %data.damageObject(%vehicleObject, 0, VectorAdd(%vec, %vehicleObject.getPosition()), + %vecLen * %data.speedDamageScale, $DamageType::Ground); + + // associated "crash" sounds + if(%vecLen > %vDataBlock.hardImpactSpeed) + %vehicleObject.playAudio(0, %vDataBlock.hardImpactSound); + else if(%vecLen > %vDataBlock.softImpactSpeed) + %vehicleObject.playAudio(0, %vDataBlock.softImpactSound); +} + +//************************************************************** +//* VEHICLE TIMEOUTS +//************************************************************** + +function vehicleAbandonTimeOut(%vehicle) +{ + if(%vehicle.getDatablock().cantAbandon $= "" && %vehicle.lastPilot $= "") + { + for(%i = 0; %i < %vehicle.getDatablock().numMountPoints; %i++) + if (%vehicle.getMountNodeObject(%i)) + { + %passenger = %vehicle.getMountNodeObject(%i); + if(%passenger.lastVehicle !$= "") + schedule(15000, %passenger.lastVehicle,"vehicleAbandonTimeOut", %passenger.lastVehicle); + %passenger.lastVehicle = %vehicle; + %vehicle.lastPilot = %passenger; + return; + } + + if(%vehicle.respawnTime !$= "") + %vehicle.marker.schedule = %vehicle.marker.data.schedule(%vehicle.respawnTime, "respawn", %vehicle.marker); + %vehicle.mountable = 0; + %vehicle.startFade(1000, 0, true); + %vehicle.schedule(1001, "delete"); + } +} + +//------------------------------------------------------------------------------ +function HoverVehicleData::create(%block, %team, %oldObj) +{ + if(%oldObj $= "") + { + %obj = new HoverVehicle() + { + dataBlock = %block; + respawn = "0"; + teamBought = %team; + team = %team; + }; + } + else + { + %obj = new HoverVehicle() + { + dataBlock = %block; // z0dd - ZOD, 5/18/03. Dynamix had param named %data, screwed up creation + respawn = %oldObj.respawn; // z0dd - ZOD, 4/10/04. Fixed, was 0 + teamBought = %team; + team = %team; + mountable = %oldObj.mountable; + disableMove = %oldObj.disableMove; + resetPos = %oldObj.resetPos; + respawnTime = %oldObj.respawnTime; + marker = %oldObj; + }; + } + return(%obj); +} + +function WheeledVehicleData::create(%data, %team, %oldObj) +{ + if(%oldObj $= "") + { + %obj = new WheeledVehicle() + { + dataBlock = %data; + respawn = "0"; + teamBought = %team; + team = %team; + }; + } + else + { + %obj = new WheeledVehicle() + { + dataBlock = %data; + respawn = %oldObj.respawn; // z0dd - ZOD, 4/10/04. Fixed, was 0 + teamBought = %team; + team = %team; + mountable = %oldObj.mountable; + disableMove = %oldObj.disableMove; + resetPos = %oldObj.resetPos; + deployed = %oldObj.deployed; + respawnTime = %oldObj.respawnTime; + marker = %oldObj; + }; + } + + return(%obj); +} + +function FlyingVehicleData::create(%data, %team, %oldObj) +{ + if(%oldObj $= "") + { + %obj = new FlyingVehicle() + { + dataBlock = %data; + respawn = "0"; + teamBought = %team; + team = %team; + }; + } + else + { + %obj = new FlyingVehicle() + { + dataBlock = %data; + respawn = %oldObj.respawn; // z0dd - ZOD, 4/10/04. Fixed, was missing this param + teamBought = %team; + team = %team; + mountable = %oldObj.mountable; + disableMove = %oldObj.disableMove; + resetPos = %oldObj.resetPos; + respawnTime = %oldObj.respawnTime; + marker = %oldObj; + }; + } + + return(%obj); +} + +function FlyingVehicleData::switchSidesSetPos(%data, %oldObj) +{ + %team = %oldObj.curTeam == 1 ? 2 : 1; + %oldObj.curTeam = %team; + %obj = new FlyingVehicle() + { + dataBlock = %data; + respawn = %oldObj.respawn; // z0dd - ZOD, 4/10/04. Was missing this param + teamBought = %team; + team = %team; + mountable = %oldObj.mountable; + disableMove = %oldObj.disableMove; + resetPos = %oldObj.resetPos; + respawnTime = %oldObj.respawnTime; + marker = %oldObj; + }; + %obj.setTransform(%oldObj.getTransform()); + + return(%obj); +} + +function WheeledVehicleData::switchSidesSetPos(%data, %oldObj) +{ + %team = %oldObj.curTeam == 1 ? 2 : 1; + %oldObj.curTeam = %team; + %obj = new WheeledVehicle() + { + dataBlock = %data; + respawn = %oldObj.respawn; // z0dd - ZOD, 4/10/04. Lagg_Alot - Fixed, was 0 + teamBought = %team; + team = %team; + mountable = %oldObj.mountable; + disableMove = %oldObj.disableMove; + resetPos = %oldObj.resetPos; + deployed = %oldObj.deployed; + respawnTime = %oldObj.respawnTime; + marker = %oldObj; + }; + %obj.setTransform(%oldObj.getTransform()); + return(%obj); +} + +// z0dd - ZOD, 4/10/04. Lagg_Alot - Function was named incorrectly +//function HoverVehicleData::switchSides(%data, %oldObj) +function HoverVehicleData::switchSidesSetPos(%data, %oldObj) +{ + %team = %oldObj.curTeam == 1 ? 2 : 1; + %oldObj.curTeam = %team; + %obj = new HoverVehicle() + { + dataBlock = %data; + respawn = %oldObj.respawn; // z0dd - ZOD, 4/10/04. Lagg_Alot - Fixed, was 0 + teamBought = %team; + team = %team; + mountable = %oldObj.mountable; + disableMove = %oldObj.disableMove; + resetPos = %oldObj.resetPos; + respawnTime = %oldObj.respawnTime; + marker = %oldObj; + }; + %obj.setTransform(%oldObj.getTransform()); + return(%obj); +} + +function resetNonStaticObjPositions() +{ + MissionGroup.setupPositionMarkers(false); + MissionCleanup.positionReset(); +} + +function next(%team) +{ + ResetObjsPositions(%team); +} + +function SimGroup::positionReset(%group) +{ + for(%i = %group.getCount() - 1; %i >=0 ; %i--) + { + %obj = %group.getObject(%i); + if(%obj.resetPos && %obj.getName() !$= PosMarker) + %obj.delete(); + else + %obj.positionReset(); + } + + for(%i = 0; %i < %group.getCount(); %i++) + { + %obj = %group.getObject(%i); + if(%obj.getName() $= PosMarker) + { + cancel(%obj.schedule); + %newObj = %obj.data.switchSidesSetPos(%obj); + MissionCleanup.add(%newObj); + setTargetSensorGroup(%newObj.target, %newObj.team); + } + else + %obj.positionReset(); + } +} + +function VehicleData::respawn(%data, %marker) +{ + %mask = $TypeMasks::PlayerObjectType | $TypeMasks::VehicleObjectType | $TypeMasks::TurretObjectType; + InitContainerRadiusSearch(%marker.getWorldBoxCenter(), %data.checkRadius, %mask); + if(containerSearchNext() == 0) + { + %newObj = %data.create(%marker.curTeam, %marker); + %newObj.startFade(1000, 0, false); + %newObj.setTransform(%marker.getTransform()); + + setTargetSensorGroup(%newObj.target, %newObj.team); + MissionCleanup.add(%newObj); + } + else + { + %marker.schedule = %data.schedule(3000, "respawn", %marker); + } +} + +function SimObject::positionReset(%group, %team) +{ + //Used to avoid warnings +} + +function Terraformer::positionReset(%group, %team) +{ + //Used to avoid warnings +} + +function SimGroup::setupPositionMarkers(%group, %create) +{ + for(%i = %group.getCount() - 1; %i >= 0; %i--) + { + %obj = %group.getObject(%i); + if(%obj.resetPos || %obj.respawnTime !$= "") + { + if(%create) + { + %marker = %obj.getDataBlock().createPositionMarker(%obj); + MissionCleanup.add(%marker); + %obj.marker = %marker; + } + else + { + %obj.delete(); + } + } + else + %obj.setupPositionMarkers(%create); + } +} + +function SimObject::setupPositionMarkers(%group, %create) +{ + //Used to avoid warnings +} + +function VehicleData::createPositionMarker(%data, %obj) +{ + %marker = new Trigger(PosMarker) + { + dataBlock = markerTrigger; + mountable = %obj.mountable; + disableMove = %obj.disableMove; + resetPos = %obj.resetPos; + data = %obj.getDataBlock().getName(); + deployed = %obj.deployed; + curTeam = %obj.team; + respawnTime = %obj.respawnTime; + }; + %marker.setTransform(%obj.getTransform()); + return %marker; +} + +function VehicleData::hasDismountOverrides(%data, %obj) +{ + return false; +} +